Сервис 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: [] }).
Сервис LegacyXml2SelectDb (LegacyXml2SelectDb.mjs)
Наследует: ServiceRequire
Назначение: Предоставляет низкоуровневые методы для взаимодействия с БД, необходимые при разборе свойств документов (метаданные, справочники, функции).
Зависимости
-
QueriesHelper– обязательный.
Методы
Каждый метод получает log и userId для логирования и контекста прав.
| Метод | Параметры | Возвращаемое значение | Описание |
|---|---|---|---|
getPropKind(log, userId, docpropId) |
docpropId: number |
Тип свойства (строка?) | Получает «kind» свойства документа (через пакет CSP_XML2SELECT.GET_PROP_KIND). |
getPropKindType(log, userId, docpropId) |
docpropId: number |
Число – тип kind | Получает числовой тип свойства (CSP_XML2SELECT.GET_PROP_KIND_TYPE). |
listClassifierResources(log, userId, docPropValue) |
docPropValue: string |
Массив строк‑описаний ресурсов | Иерархический поиск ресурсов классификатора по значению. |
getUsername(log, userId) |
– | Строка (ФИО) или null |
Возвращает полное имя текущего пользователя. |
getRoleId(log, userId) |
– | Число – ID роли | Вызывает documents.GET_ROLE_ID(). |
getRoleName(log, userId) |
– | Строка или null |
Название роли из справочника. |
getDepartId(log, userId) |
– | Строка или null |
Идентификатор отдела пользователя. |
execFunc(log, userId, dbFunc) |
dbFunc: string – имя функции (например, MY_FUNC()) |
Результат выполнения | Выполняет SQL‑функцию через SELECT. |
execDate(log, userId, dateFunc) |
dateFunc: string – выражение даты (например, SYSDATE) |
Строка даты в формате DD.MM.YYYY^HH24:MI:SS или null |
Преобразует выражение даты в строку. |
getDocPropKind(log, userId, docpropId) |
docpropId: number |
Значение docprop_kind или null |
Возвращает тип поля свойства (из model_documents.doc_kind_properties). |
listEvents(log, userId, docKindId) |
docKindId: number |
Массив ID статусов | Список глобальных и связанных с видом документа статусов (событий). |
getSelectLevel(log, userId, docpropId) |
docpropId: number |
Число (уровень зависимости) | Уровень вложенности зависимостей свойства (CSP_XML2SELECT.GET_SELECT_LEVEL). |
getVocabIdByDocPropId(log, userId, docpropId) |
docpropId: number |
ID словаря или null |
Получает идентификатор словаря, привязанного к свойству. |
listVocabCodes(log, userId, vocabId, codes) |
vocabId: number, codes: string[] |
Массив кодов | Возвращает существующие в словаре коды (точное совпадение). |
listVocabCodesHierarchical(log, userId, vocabId, code) |
vocabId: number, code: string |
Массив кодов (включая дочерние) | Иерархический поиск кодов словаря (родитель‑потомок). |
Все методы используют 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– идентификатор вида документа. -
level– уровень вложенности в дереве ссылок.
Логика работы:
- Извлекает метаданные свойства через
#getPropFromXml. - Определяет SQL‑оператор по атрибуту
condition(например,'e'→'='/'IN'). - Обрабатывает макросы в значениях (
@USERID,@DATEи др.) через#processMacros. - Если
docPropId === -2, строит условие для фильтрации по ID документов (#buildDocIdCondition). - Для остальных свойств создаёт подзапрос
EXISTSкvalue_documents.doc_properties, присоединяет зависимости согласноselectLevel. - Добавляет специфичные условия в зависимости от типа свойства (
kindType):- Классификаторы (
docPropKindId === 20), - Словари (
kindType === 4), - Скалярные типы (строки, числа, даты) – через
#buildScalarConditions.
- Классификаторы (
- Возвращает
WhereConditionтипа'exists', содержащий собранный подзапрос.
Внутренние вспомогательные методы описаны в исходном коде.
Класс MacroParser
Файл: MacroParser.mjs
Назначение: Простейший рекурсивный парсер для разбора макрос‑строк, содержащих вызовы функций, числа, строки и идентификаторы.
Конструктор
constructor(input: string)
Инициализирует позицию и текущий символ.
Метод parse()
Парсит выражение и возвращает AST‑узел.
Возвращаемый объект (примеры):
-
{ 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)
Тип type |
Назначение param |
|---|---|
'simple' |
Строка SQL‑выражения (напр., "age > 18") |
'exists' |
Экземпляр SqlQuery (подзапрос) |
'and' / 'or' |
Массив WhereCondition[] (опционально) |
'not' |
Единственный WhereCondition |
Основные методы:
-
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, который обходит все ссылки, свойства, статусы и операции. - Для каждого свойства вызывается
PropertyBuilder.buildPropCondition, который может выполнять запросы к БД черезLegacyXml2SelectDbдля получения метаданных (например,getPropKind,listClassifierResources,listVocabCodesHierarchical). Результат добавляется в деревоWhereCondition. - После обхода вызывается
mainQuery.where.optimize()для упрощения, и готовыйSqlQueryвозвращается. - При необходимости клиент может вызвать
query.build()для получения SQL‑строки или использовать объектSqlQueryдля дальнейших модификаций.
Примечания и ограничения
-
LegacyXml2SelectInnerPgсодержит закомментированную логику дляHAVINGс подсчётом (counter), которая выбрасывает исключение. Это указывает на незавершённую реализацию. - Метод
#parseFormalReferenceимеет неполную реализацию дляouterJoinв режимах 4‑6 (возвращает пустоеWhereConditionвместо корректного условия JOIN). - Схема сервиса (
service-schema.mjs) расходится с реальными методами:parseиparseDbне реализованы, вместо них используютсяparseToObjиparseToSql. Рекомендуется привести схему в соответствие. -
LegacyXml2SelectDbиспользует прямые SQL‑запросы черезQueriesHelper, предполагая наличие определённых хранимых процедур и пакетов (CSP_XML2SELECT,documents,vocabs). - Классификаторы и словари требуют корректной настройки справочников в БД.
- Макросы поддерживают ограниченный набор подстановок; для новых переменных потребуется расширение
#processMacrosвPropertyBuilder.
Пример использования (псевдокод)
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" condition="e" value="test"/></formal></query>',
queryMode: 0,
permissions: 1
}, null, log);
console.log(sql); // Сгенерированный SQL SELECT ...