Skip to main content

Сервис 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>:

МеАтодрибут ПОбязарамтетрльный Возвращаемое значенТиеп Описание
getPropKind(log, userId, docpropId)dockind_id 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Массив строк‑описаний ресурсовinteger Иерархический поиск ресурсов классификатора по значению.
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: numberID словаря или 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 – идентификатор вида документа.
  • (dockind_id) целевой таблицы levelname нет and/or Логическая операция, объединяющая все условия верхнего уровня (по умолчанию and)

    2. Элеменьты внутри <formal> и <reference>

    Допускаются сложедующие элеменнты (в любостм порядке и в деревелюбых сочетаниях):

  • Опреде
  • Д
  • ЭлементНазначение
    <reference>Ссылок.

    Логика на драбуготы:

    й
    1. Извлдокумекаент / тип документад (JOIN / EXISTS)
    <property>Условие на значеныие свойства чдокумерезнта
    #getPropFromXml<status>. Филяеьт SQL‑оператор по статрибутсу condition(виду (например, 'e' → '=' / 'IN').
  • Особрабаытывает макросы в значениях (@USERID, @DATE и др.) через #processMacros.
  • Если docPropId === -2, строит условие для фильтрации по ID документа
  • <operation>Логическая группа условий (#buildDocIdConditionAND / OR).
    <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>

    о в исходном коде.


    Класс MacroParser

    Файл: MacroParser.mjs
    Наз

    строкиии string)

    И

    Атодырибут Обязат.Типисаны Значение: Простейший рекурсивный парсер для разбора/ макрос‑строк, содержащих вызовы функций, чОписла,ние
    docprop_idдаintegerИдентификатор свойства‑ссы.

    лки

    Кон(из model_documents.doc_kind_properties)

    referencingдастрокаjoin – обычная связь, проверяется существование связанного документа;
    is null – анти‑связь, условие NOT EXISTS (связанного документа нет)
    dockind_idдаintegerИдентификатор
    вида документа, на который ссылаемся
    constructor(input:condition нет l,nl,g,ng,e,ne Условие сравнениця количества связанных документов (используется вместе с count; реализовано частиручно – вызывает пошибку)
    countнетintegerОжидаемое количество документов для сравнения (см. condition)
    valuevarнетстрокаЗарезервициюровано; ив текущией реализации не исимвпол.ьзуется
    leftpнетстрокаЗарезервировано; не используется
    rightpнетстрокаЗарезервировано; не используется

    Логика работы:

    М
    • Если внутри <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)

    Тип 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) – текущий уровень вложенности.

    Взаимодействие компонентов

    1. LegacyXml2Select получает XML и через LegacyXml2SelectInnerPg.xml2Select() создаёт объект SqlQuery.
    2. Внутри xml2Select XML преобразуется в объект, определяется dockind_id, формируются начальные SELECT/FROM/WHERE в зависимости от queryMode.
    3. Вызывается #parseFormal, который обходит все ссылки, свойства, статусы, другие ссылки).
    4. При referencing="join" и режимах 4–6 (специальные режимы отчётов) возможен сценарий с OUTER JOIN, но детали опрерацделяются вложенными условиями.
    5. Д

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_properties
conditionдастрокаКод операции сравнения (см. таблицу ниже)
doc_prop_valueнет*строкаНепосредственное значение для сравнения. Допускается использование макросов (см. раздел 6). *Обязательно, если только не используется PropertyBuilder.buildPropConditionIS NULL/IS NOT NULL.
valuevarнетстрокаСсылка на переменную или макрос; если задана и condition != 'DIRECT', кпоторый дможеняет собой doc_prop_value.
valueнетстрокаАльтернативыпнолнятье значение (напросы к БД чимерез LegacyXml2SelectDb, для работы со словарями; приоритет ниже doc_prop_value).
typeнетсимволТип свойства (берётся из БД, если не указан):
s – строка, t – текст, n – число, d – дата, v – ссылка на словарь и т.п.
kindtypeнетintegerПодтип свойства (берётся из БД). Влияет на получстроение условия метаданных (например, getPropKind4 – иерархический словарь, 10 – мультизначное свойство).

Коды операций (condition) и их SQL‑эквиваленты:

КодSQL операторПримечание
e= или ININ используется для свойств типа v, listClassifierResourcest и kindtype=4 (словарь)
ne<> или NOT INаналогично
l<
nl>=
g>
ng<=
likeLIKE
not likeNOT LIKE
inINТолько для строковых типов (s, listVocabCodesHierarchicalt), если kindtype != 4
dDIRECTСпециальный режим: прямое присоединение таблицы свойств без EXISTS (используется для docprop_id=-2)
is nullIS NULL
is not nullIS NOT NULL
minMIN(зарезервировано, используется в агрегациях)
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"/>
    

    Использованияе (пмакросевдокод)

    а
    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:
    0, permissions: 1 }, null, log); console.log(sql); // Сгене

    Группировканны условий 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)"/>