Сервис LegacyXml2Select
LegacyXml2Select – это сервис, наследующий ServiceRequire, который предоставляет возможность парсить XML-запросы, сформированные в стиле «формальных выборок», и генерировать соответствующие SQL SELECT-запросы к документам в БД. Основное назначение – обеспечить обратную совместимость со старыми механизмами поиска.
Сервис состоит из нескольких компонентов:
- LegacyXml2Select – фасад, предоставляющий внешние методы.
- LegacyXml2SelectDb – сервис‑помощник для доступа к базе данных (получение метаданных, выполнение функций).
- LegacyXml2SelectInnerPg – ядро парсинга XML и построения SQL.
- PropertyBuilder – построитель условий для свойств документов.
- MacroParser – парсер макросов (функции, литералы).
- SqlQuery / WhereCondition – утилиты для программного конструирования SQL-выражений и оптимизации WHERE-условий.
Все компоненты располагаются в одном модуле и экспортируются как единое целое.
Сервис LegacyXml2Select (index.mjs)
Наследует: ServiceRequire (из @morphcluster/core)
Назначение: Принимает XML‑строку, параметры режима запроса и прав доступа, преобразует её в объект SqlQuery (или сразу в SQL‑строку) и возвращает результат.
Зависимости
-
QueriesHelper– обязательный (указан вrequirements), предоставляет доступ к выполнению запросов к PostgreSQL. -
LegacyXml2SelectDb– обязательный (добавляется как вложенный сервис в конструкторе).
Схема запросов (service-schema.mjs)
Файл схемы содержит описание трёх запросов:
parse– требуетxml(string), возвращает объект{}(в коде соответствует методparseToObj, см. Примечание)parseDb– аналогиченparse, но с дополнительным взаимодействием с БД (в коде методаparseDbнет; вероятно, методparseToSqlиспользует БД)macroParse– требуетmacro(string), возвращает дерево разбора макроса
Примечание: В текущей реализации класс LegacyXml2Select определяет методы parseToObj, parseToSql, macroParse. Методы parse и parseDb в схеме, по-видимому, являются альтернативными или устаревшими именами. Рекомендуется обновить схему для однозначности.
Конфигурация
Параметр config передаётся в конструктор и доступен через this.config. Специфических опций не требуется.
Жизненный цикл
Конструктор создаёт и регистрирует в том же хосте экземплярLegacyXml2SelectDb(как сервис с именем"LegacyXml2SelectDb").При старте (start) вызывает.super.start()
Методы (запросы)
parseToObj({ session, xml, queryMode=0, permissions=1 })
Преобразует XML в объект SqlQuery (без построения финального SQL‑текста).
Параметры:
-
session(объект, опционально) – должен содержатьuserId, используется для фильтрации прав. -
xml(string) – XML‑строка запроса. -
queryMode(number, по умолчанию0) – режим запроса:-
0– полный запрос (выбирает поля документа), -
1– подсчёт количества (COUNT), -
2– только идентификаторы документов, -
3‑6– специальные режимы (логика меняется).
-
-
permissions(number, по умолчанию1) – уровень проверки прав доступа (-1– без проверки,1– стандартная).
Возвращает: экземпляр SqlQuery.
parseToSql({ session, xml, queryMode=0, permissions=1 })
Выполняет parseToObj, а затем вызывает sql.build(), возвращая готовый SQL‑запрос.
Параметры: те же, что у parseToObj.
Возвращает: строку SQL.
macroParse({ macro })
Парсит строку‑макрос в абстрактное синтаксическое дерево.
Параметры:
-
macro(string) – строка, содержащая вызов функции, литерал или идентификатор.
Возвращает: объект с полем tree – AST макроса (например, { type: 'function', value: 'NOW', args: [] }).
Формат XML‑запроса для LegacyXml2Select
Сервис LegacyXml2SelectDbLegacyXml2Select (LegacyXml2SelectDb.mjs)
Нпреобраследзует: ServiceRequire
НXML‑описазначение: Предоставляет низкоуровневые методы для взаимодействия с БД, необходимые при разборе свойствки документов (в SQL‑запрос SELECT.
Корневой элементаданные — <query>, спвнутрави кочники, функции).
Зависимтости
QueriesHelper–рого обязательный.
Методы
Каждый метод полрисучатствует log иэлемент , задuserId<formal>ляающий услогирования фильтрации.
1. Корнтеквая ста пруктура
<?xml version="1.0"?>
<query>
<formal dockind_id="ID_ВИДА_ДОКУМЕНТА" [name="and|or"]>
<!-- набор услов.ий -->
</formal>
</query>
Атрибуты <formal>:
| Описание | |||
|---|---|---|---|
|
да |
| |
| | | |
| | И | |
| | ||
| | ||
| | ||
| | ||
| | | |
| | | |
| | | |
| | ||
| | | |
| | | |
| | ||
| |
Все методы используют QueriesHelper.query() или querySql() для выполнения запросов.
Класс LegacyXml2SelectInnerPg
Файл: LegacyXml2SelectInnerPg.mjs
Назначение: Содержит основную логику преобразования XML в SQL‑запрос для PostgreSQL. Использует экземпляр LegacyXml2SelectDb для получения метаданных, PropertyBuilder для условий свойств, и SqlQuery / WhereCondition для построения запроса.
Конструктор
constructor(Xml2SelectDb)
Xml2SelectDb– экземплярLegacyXml2SelectDb(или совместимый объект).
Создаёт экземпляр PropertyBuilder, инициализирует пустой SqlQuery (mainQuery), и устанавливает начальные значения queryMode, permissions, userId, log.
Методы
xml2Select(xml, queryMode = 0, permissions = 0, userId, log)
Главный публичный метод. Парсит XML, обходит его формальную часть, конструирует запрос и возвращает SqlQuery.
Параметры:
xml(string) – XML‑строка с корневым элементом<query><formal dockind_id="...">...</formal></query>.queryMode– режим запроса.permissions– уровень проверки прав.userId– идентификатор пользователя.log– объект логгера.
Возвращает: SqlQuery.
Внутренние вспомогательные методы
#parseXml(xml)– парсит XML‑строку с помощьюfast-xml-parser. Добавляет XML‑декларацию, если отсутствует. Возвращает JS‑объект.#getRefs(xmlRef)– извлекает атрибуты ссылки (docprop_id,referencing,dockind_idи т.д.).#buildReferenceSubquery({ refPropId, referencing, docKindId, condition, counter }, level)– создаёт подзапросEXISTSдля проверки ссылок между документами. Добавляет условия прав, если необходимо.#buildStatusCondition(statList, level)– формирует условиеINдля фильтрации по статусам.#parseFormalReference(xmlRef, parentDockindId, operName, level, outerJoin)– обрабатывает одну ссылку в формальном блоке, рекурсивно обходит вложенные ссылки, свойства, статусы, операции. ВозвращаетWhereCondition.#parseFormal(xmlObj, docKindId, level, outerJoin)– обходит формальный блок XML и возвращает агрегированноеWhereCondition, объединяя все ссылки, свойства, статусы и операции через AND/OR.
Все приватные методы активно используют PropertyBuilder для построения условий свойств.
Класс PropertyBuilder
Файл: PropertyBuilder.mjs
Назначение: Строит условия WHERE для свойств документов на основе их XML‑описания и метаданных, полученных из Xml2SelectDb.
Конструктор
constructor(Xml2SelectDb)
Принимает экземпляр LegacyXml2SelectDb.
Основной метод
buildPropCondition(log, userId, xmlProp, docKindId, level)
Возвращает Promise<WhereCondition> – условие для одного свойства.
Параметры:
log,userId– стандартные.xmlProp– XML‑узел свойства (содержит@_docprop_id,@_type,@_conditionи т.д.).docKindId– идентификатор вида документа.- (
dockind_id) целевой таблицыlevelname–нет and/orЛогическая операция, объединяющая все условия верхнего уровня (по умолчанию and)
2. Элемен
ьты внутри<formal>и<reference>Допускаются сл
ожедующие элеменнты (в любостм порядке и вдеревелюбых сочетаниях):Элемент Назначение <reference>Ссыл ок.
йЛогика на драбуготы:Извлдокумекаент / тип документад(JOIN / EXISTS)
<property>Условие на значен ыие свойствачдокумерезнта#getPropFromXml<status>.ОпредеФил яеьтSQL‑оператор по статрибутсу(видуcondition(например,'e'→'='/'IN').Особрабаытывает макросы в значениях (@USERID,@DATEи др.)через#processMacros.ЕслиdocPropId === -2, строит условие для фильтрации по IDдокумента<operation>Логическая группа условий ( /#buildDocIdConditionANDOR).Д<select>(Зарезервирован; влияет на логику outer join, но вывод не меняет) Все они могут вкладываться внутрь
<reference>и<operation>.
3. Элемент
<reference>Описывает переход к связанному документ
альныху через свойство‑ссылку. Реализуется либоздкаёт подзапроскEXISTS/NOT EXISTS, либо какvalue_documents.doc_propertiesJOIN,присоединяет зависимости согласноselectLevel. Добавляет специфичные условия(в зависимости оттипа свойства (kindType):Классификаторы (docPropKindId === 20),Словари(kindType === 4),Скалярныебутипы (строки, числа, даты) – через#buildScalarConditions.
ВозвращаетWhereConditionтипа'exists'referencing,соди рержимащийсобранный подзапроса).
<reference
docprop_id="ID_СВнуОЙСТВА_ССЫЛКИ"
referencing="join|is null"
dockind_id="ID_ЦЕЛЕВОГО_ВИДА"
[condition="l|nl|g|ng|e|ne"]
[count="целое_число"]
[valuevar="строка"]
[leftp="строка"]
[rightp="строка"]
>
<!-- вложенние вспомогательные меreference, property, status, operation -->
</reference>
| Ат |
Обязат. | Тип |
Значени |
|---|---|---|---|
docprop_id |
да | integer | Идентификатор свойства‑ссы
|
referencing |
да | строка | join – обычная связь, проверяется существование связанного документа;is null – анти‑связь, условие NOT EXISTS (связанного документа нет) |
dockind_id |
да | integer | Идентификатор вида документа, на который ссылаемся |
|
нет | l,nl,g,ng,e,ne |
Условие сравнениcount; реализовано части |
count |
нет | integer | Ожидаемое количество документов для сравнения (см. condition) |
valuevar |
нет | строка | Зарезерви |
leftp |
нет | строка | Зарезервировано; не используется |
rightp |
нет | строка | Зарезервировано; не используется |
Логика работы:
М
- Если внутри
<reference> нетод parse()
<reference> нетparse()Парснит выраложенных <reference>, <property>, <status>, <operation>, ние <select>, и режим запроса = 4–6, то такой элемент при referencing="join" или внешнем созединении превращается AST‑в узсловиел 1=1.
озвращаемый объстальных случаях для <reference> гекнерируется подзапрос EXISTS (или NOT EXISTS при referencing="is null"), в котором проверяется наличие связанных докумернтов и дополнительно наклады):
{ type: 'string', value: 'abc' }{ type: 'number', value: 123 }{ type: 'identifier', value: 'USER' }{ type: 'function', value: 'NOW', args: [ ... ] }
Поддерживаеются вложенные вызовы: FUNC(1, 'x', OTHER(2)).
Классы SqlQuery и WhereCondition
Файл: SqlQuery.mjs
Назначение: Программное построение SQL‑запросов с удобным API и оптимизацией условий.
WhereCondition
Конструктор: new WhereCondition(type = 'and', param = null)
| |
|---|---|
| |
| |
| |
| |
Основные методы:
add(condition)– добавить условие в группу.addSimple(expr)– добавить простое условие.addExists(subquery),addNotExists(subquery)– EXISTS / NOT EXISTS.addNot(condition)– добавить отрицание.addOr(conditions),addAnd(conditions)– добавить подгруппу.optimize()– возвращает оптимизированное дерево (удаление избыточных вложений, двойного отрицания, TRUE/FALSE свёртки).build(options)– рекурсивно генерирует SQL‑строкуусловия.
SqlQuery
Представляет полный SQL‑запрос.
Свойства:
query.select– массив выражений для SELECT.query.from– массив таблиц/подзапросов.query.joins– массив JOIN’ов.query.orderBy,query.groupBy,query.limit,query.offset– соответствующие части запроса.where– корневой(WhereConditionпо умолчанию пустая'and'группа).
Методы (возвращают this для цепочек):
select(exprs)– добавить колонки.from(table, alias?)– добавить таблицу илиSqlQueryкак подзапрос.innerJoin(...),leftJoin(...),addJoin(type, ...)– присоединения (поддерживают LATERAL).orderBy(columns),groupBy(columns),limit(limit, offset?)– сортировка, группировка, лимит.build(options)– собирает SQL‑строку.buildPretty(options)– то же с форматированием.
Опции build:
pretty(boolean, по умолчаниюfalse) – включить отступы.indentSize(number, по умолчанию2) – размер отступа.isSubquery(boolean) – внутренний флаг для правильной обработки подзапроса.indent(number) – текущий уровень вложенности.
Взаимодействие компонентов
LegacyXml2Selectполучает XML и черезLegacyXml2SelectInnerPg.xml2Select()создаёт объектSqlQuery.Внутриxml2SelectXML преобразуется в объект, определяетсяdockind_id, формируются начальные SELECT/FROM/WHERE в зависимости отqueryMode.Вызывается#parseFormal, который обходит все ссылки,свойства, статусы, другие ссылки).- При
referencing="join"и режимах 4–6 (специальные режимы отчётов) возможен сценарий сOUTER JOIN, но детали опрерацделяются вложенными условиями. Д
4. Эл
яемент<property>Задаёт фильтр по значению к
аждонкретного свойства документа (или по служебному идентификатору).<property docprop_id="ID_СВОЙСТВА" condition="код_операции" [doc_prop_value="значение"] [valuevar="макрос"] [value="значение"] [type="s|t|n|d|v"] [kindtype="число"] />Атрибут Обязат. Тип Описание / Возможные значения docprop_idда integer Идентификатор свойства документа. Особые значения: -2– ID документа (прямая выборка по document_id);-4– дата события (event_date);-5,-6– пользователь, породивший событие (event_user);-7– описание документа (doc_descr). Остальные числа – обычные свойства изdoc_kind_propertiesconditionда строка Код операции сравнения (см. таблицу ниже) doc_prop_valueнет* строка Непосредственное значение для сравнения. Допускается использование макросов (см. раздел 6). *Обязательно, если только не используется /PropertyBuilder.buildPropConditionIS NULLIS NOT NULL.valuevarнет строка Ссылка на переменную или макрос; если задана и condition != 'DIRECT',кпоторыйдможеняет собойdoc_prop_value.valueнет строка Альтернатив ыпнолнятье значение (напросы к БД чимерез, для работы со словарями; приоритет нижеLegacyXml2SelectDbdoc_prop_value).typeнет символ Тип свойства (берётся из БД, если не указан): s– строка,t– текст,n– число,d– дата,v– ссылка на словарь и т.п.kindtypeнет integer Подтип свойства (берётся из БД). Влияет на по лучстроение условияметаданных(например,– иерархический словарь,getPropKind410– мультизначное свойство).Коды операций (
condition) и их SQL‑эквиваленты:Код SQL оператор Примечание e=илиININиспользуется для свойств типаv,иlistClassifierResourcestkindtype=4(словарь)ne<>илиNOT INаналогично l<nl>=g>ng<=likeLIKEnot likeNOT LIKEinINТолько для строковых типов ( s,), еслиlistVocabCodesHierarchicaltkindtype!= 4dDIRECTСпециальный режим: прямое присоединение таблицы свойств без EXISTS(используется дляdocprop_id=-2)is nullIS NULLis not nullIS NOT NULLminMIN(зарезервировано, используется в агрегациях) maxMAX(зарезервировано) Особые
docprop_id:-
-2(DIRECT): фильтр по идентификаторам документов.-
doc_prop_value– список ID через запятую, например"10,20,30". -
valuevar– имя функции, возвращающей массив ID (вызывается черезXml2SelectDb.execFunc).
-
-
-4: фильтр по дате события (event_date).РДляcondition='<=...'значезние автоматически корректируется на конец дня. -
-5,-6: фильтр по пользователю события (event_user). -
-7: фильтр по текстовому описанию (doc_descr). Для операторовLIKE/NOT LIKE– сравнение без учёта регистра.
5. Элемент
<status>Фильтрация по статусу (виду события) документа.
<status doceventkind_id="ID_СОБЫТИЯ" />Может встречаться многократно. Каждый экземпляр задаёт одно значение
doceventkind_id.
В SQL формируется условие:
dt_{level}.doceventkind_id IN (0, список_ID_событий)Если список событий в XML совпадает с полным набором событий для данного вида документа (или полный набор пуст), условие не добавляется
в(чтобыдне перевгружать запрос избыточным перечислением).
6. Элемент
<operation>Группирует несколько условий с заданной логической связкой.
WhereCondition<operation name="and|or"> <!-- reference, property, status, другие operation --> </operation>-
name="and"– все вложенные условия объединяются черезAND. Пname="or"– черезOR.- Атрибут
nameмослежно обхпустить; тогда элемент становится прозрачным контейнером (вложенные условия просто передаются на уровень выше без добавления собственной группы).
7. Элемент
<select>(зарезервирован)Присутствует в коде, но не влияет на итоговый SQL. Его наличие/отсутствие используется
толькоmainQuery.where.optimize()дво внутренней ляогикеуопрощеделения, нужно лиготовыйSqlQueryвсозврдащается.-
При необходимости клиент может вызвать.query.build()OUTER JOIN
Пример:<select />В текущей реализации его сод
лержимое игнорируется.п
8. По
лудстановка макросов в значенияхSQL‑сВ атрибутах
doc_prop_value,valuevar,value(а также@_valuevarу<reference>) моки илигут использоваться макробъсы.
Поддерживаются следующие макросы (регистр важен):Макрос Подстановка @USERIDID текущего пользоват SqlQueryделяда(documents.get_user_id)@USERNAMEПолное имя поль нзователя (Фамилия Имя Отчество)@ROLEIDID текущей шролихмпользователя (documents.get_role_id)@ROLENAMEНазвание роли @DEPARTIDИдентифика ций.торПподримаздечаленияипользователя@DATEТекущая дата/время ( SYSDATE); мограничженияLegacyXml2SelectInnerPgсодержитзакоммебинтированную логику дляHAVINGс подсчётом (counter), которая выбрасывает исключение. Это указывает на незавершённую реализацию.Метод#parseFormalReferenceимеет неполную реализацию дляouterJoinв режимах 4‑6 (возвращает пустоеWhereConditionвместо корректного условия JOIN).Схема сервиса (service-schema.mjs) расходиться среальфунымкциметодаями:,parse@DATE+1@DATE-7и т.п.
parseDbGET_USER_FILIALSне реалиВызов афуны, вместо нкцих используютсяparseToObjиparseToSqlGET_USER_FILIALS.Р(и другиекомендуетсяприводобныести)схемуNOW,TODAY,WORKDAY,MONTHDAY,*YEAR*Вызов соответств ие.LegacyXml2SelectDbиспользует прямые SQL‑запросы черезQueriesHelper, предполагая наличие определённых хранимых процедур и пакетов (CSP_XML2SELECT,documents,vocabs). ,Классификаторы и словари требуют коррщектной фунастройкцисправочников вБД.- возвращающей дату
Макросы распо
ддерживзнаются погрточному совпаденичю (@USERID) или по вхожденныйию в строку (набпример,TO_DATE('@DATE','DD.MM.YYYY')привордит к подстановок;едSYSDATE).Есл
яи значение новых перемеачинных потребуается с буквы и содержит скобки (например,my_func(1,2)), то оно расшсматривается как вызов функции БД и выполняется черениез#processMacrosXml2SelectDb.execFuncвили.PropertyBuilderexecDate
9. Примеры
Простейший запрос (получить все документы вида 10)
<query>
<formal dockind_id="10" />
</query>
Фильтр по свойствам и статусу
<query>
<formal dockind_id="10" name="and">
<property docprop_id="100" condition="e" doc_prop_value="Иванов"/>
<property docprop_id="101" condition="g" doc_prop_value="1000"/>
<status doceventkind_id="3"/>
<status doceventkind_id="5"/>
</formal>
</query>
Ссылка на связанный документ
<query>
<formal dockind_id="10">
<reference docprop_id="200" referencing="join" dockind_id="20">
<property docprop_id="201" condition="like" doc_prop_value="%утверждён%"/>
</reference>
</formal>
</query>
Анти‑ссылка (NOT EXISTS)
<reference docprop_id="200" referencing="is null" dockind_id="20"/>
Использованияе (пмакросевдокод)
а
<query>
<formal dockind_id="10" />
</query>
<query>
<formal dockind_id="10" name="and">
<property docprop_id="100" condition="e" doc_prop_value="Иванов"/>
<property docprop_id="101" condition="g" doc_prop_value="1000"/>
<status doceventkind_id="3"/>
<status doceventkind_id="5"/>
</formal>
</query>
<query>
<formal dockind_id="10">
<reference docprop_id="200" referencing="join" dockind_id="20">
<property docprop_id="201" condition="like" doc_prop_value="%утверждён%"/>
</reference>
</formal>
</query>
<reference docprop_id="200" referencing="is null" dockind_id="20"/>
const host = new ServiceHost('test');
const legacySvc = new LegacyXml2Select(host, {});
await host.start(log);
// Предположим, что QueriesHelper настроен и БД доступна
const sql = await legacySvc.sendRequest('parseToSql', {
session: { userId: 123 },
xml: '<query><formal dockind_id="5"><property docprop_id="10"102" condition="e" value=doc_prop_value="test"@USERID"/></formal></query>',
queryMode:
Группировканны условий SQLс SELECTOR
<operation name="or">
<property docprop_id="100" condition="e" doc_prop_value="A"/>
<property docprop_id="100" condition="e" doc_prop_value="B"/>
</operation>
Выборка по списку ID
<property docprop_id="-2" condition="d" doc_prop_value="101,205,330"/>
или через функцию:
<property docprop_id="-2" condition="d" valuevar="my_package.get_docs(55)"/>