XQuery: раскрываем потенциал
О чём эта статья
Многие разработчики привыкли использовать для работы с данными прямые SQL запросы, но что делать, если ты разрабатываешь какой-либо универсальный компонент, который должен работать на разных базах данных, или ты знаешь о грядущем переезде на другую базу данных?
В данном случае стоит присмотреться к возможностям XQuery, которые часто упускают из виду. В этой статье я расскажу о встроенных функциях, приведу несколько примеров запросов и расскажу как можно оптимизировать запросы к базе данных.
Варианты выполнения запросов к базе данных
Начнём с банального - обговорим варианты обращения к базе данных, так как нас могут читать как прожженные любители websoft, так и начинающие специалисты.
Интерфейс в приложении администратора
Если мы хотим быстро что-то посмотреть в базе данных, можно использовать специализированный инструмент, доступный в приложении администратора. Для его открытия необходимо нажать на иконку Шестерёнки в левом нижнем углу, выбрать пункт меню Поиск и подпункт По каталогам..., после чего у нас откроется интерфейс для формирования XQuery запроса.
Данный интерфейс позволяет нам как ввести запрос напрямую в поле XQuery, предварительно поставив галочку слева от названия поля, так и собрать запрос по частям, используя соответствующие поля ввода. При этом, когда мы заполняем поля с условиями, итоговый запрос автоматически прописывается в поле XQuery, что можно увидеть на скриншоте ниже - этот запрос можно скопировать и использовать в своём программном коде.
Также поделюсь небольшой хитростью с теми, кто не знает или забыл ключи полей, по которым вы планируете делать запрос - нужно просто открыть окно создания или редактирования фильтра в интересующем вас каталоге и выбрать необходимые вам условия, после чего внизу окна фильтра вы увидите поле с условиями XQuery.
Данные условия так же можно использовать в поле Условие XQuery (относительно $elem) в полях с типом Ссылка на объект в базе и некоторых выборках, позволяющих указать дополнительные условия, но это уже тема для другой статьи.
Важно помнить, что не все функции работают в данном интерфейсе и для проверки представленных в статье функций и запросов лучше использовать программный код
Вызов из программного кода
Если же нам необходимо сделать запрос к базе данных из программного кода, мы можем использоваться прямой вызов функции XQuery, или же использовать обёртку tools.xquery() - которая позволит нам применять дополнительные конструкции в запросе.
Важно помнить, что запрос выполнится только в том случае, если вы обращаетесь к его результатам
Функции в XQuery
В XQuery реализован ряд функций для упрощения выполнения выборки данных из базы.
Сontains
Функция предназначена для проверки вхождения подстроки в строку, другими словами, с помощью неё можно проверить содержит ли какое-то поле искомое нами значение.
Например, найдём сотрудников, у которых в поле code содержится значение F_:
for $elem in collaborators where contains($elem/code, "F_") return $elem
И обратный пример с выбором сотрудников, у которых в поле code отсутствует подстрока F_:
for $elem in collaborators where contains($elem/code, "F_") = false() return $elem
Doc-contains
Функция предоставляет возможность выполнять полнотекстовый поиск по документам - это довольно мощный инструмент, если уметь им правильно пользоваться.
Стандартный поиск
Простой пример поиска сотрудников с наличием в карточке слова Симинюк:
for $elem in collaborators where doc-contains($elem/id, "wt_data", "Симинюк") return $elem
И обратный пример, найдём всех сотрудников, у которых данное слово не встречается:
for $elem in collaborators where doc-contains($elem/id, "wt_data", "Симинюк") = false() return $elem
Также поддерживается поиск по конкретной фразе:
for $elem in collaborators where doc-contains($elem/id, "wt_data", "[Симинюк]") return $elem
И поиск по словоформам, который включается путём добавления символа ` в конец искомого значения, но для этого требуется дополнительная настройка:
for $elem in document where doc-contains($elem/id, "wt_data", "отдела`", "document") return $elem
Поиск по настраиваемым полям
Также данная функция позволяет искать значения по Настраиваемым полям в карточке объекта, например, мы можем найти все записи, в которых значение настраиваемого поля с кодом some_string_field содержит Искомое значение:
for $elem in collaborators where doc-contains($elem/id, "wt_data", "[some_string_field contains Искомое значение~string]") return $elem
Или, например, найти все записи, в которых значение настраиваемого поля с кодом some_date_field, имеющего тип Дата, меньше даты 2026-03-20:
for $elem in collaborators where doc-contains($elem/id, "wt_data", "[custom_field_name>2026-03-20~date]") return $elem
Fields
Позволяет вернуть конкретный набор полей в ответе, что, в теории, положительно влияет на скорость выполнения запроса.
В примере мы возвращаем только id и email сотрудника, игнорируя остальные столбцы:
for $elem in collaborators return $elem/Fields("id", "email")
Мы не можем одновременно использовать конструкцию с
$elem/Fields()и$elem/__data- только что-то одно.
ForeignElem
Функция позволяет нам обратиться к связанному объекту прямо внутри текущего запроса.
Для примера, найдём всех действующих сотрудников, которые прикреплены за действующими подразделениями:
for $elem in collaborators where $elem/is_dismiss = false() and ForeignElem($elem/position_parent_id)/is_disbanded = false() return $elem
IsHierChild / Hier
Функция позволяет получать массив вложенных объектов по указаному родительскому объекту.
Например, найдём все вложенные подразделения для родительского подразделения:
for $elem in subdivisions where IsHierChild($elem/id, 6327975429225669221) order by $elem/Hier() return $elem/id
Конструкция
order by $elem/Hier(), в данном случае, является обязательной
MatchSome
Функция предназначена для поиска по массиву значений.
Например, нам необходимо найти все мероприятия с типом Разовое мероприятие и Вебинар, для этого мы воспользуемся сравнением со списком наших значений:
for $elem in events where MatchSome($elem/type_id, ('one_time', 'webinar')) return $elem
Данную функцию так же можно использовать для исключения:
for $elem in events where MatchSome($elem/type_id, ('one_time', 'webinar')) = false() return $elem
Some / Satisfies
Функция позволяет выполнять запрос по двум таблицам, в какой-то степени является аналогом join в SQL.
Большинство разработчиков забывает как строится данная конструкция, поэтому не старайтесь её выучить, просто помните, что она есть и где её найти.
В примере ниже представлен запрос для выборки назначенных пользователю квалификаций, имеющих статус active.
for $elem in qualification_assignments where $elem/person_id =
6148914691236517121 and some $qual in qualifications satisfies ($qual/status="active" and $qual/id = $elem
/qualification_id) return $elem
Оптимизация запросов
Универсальность - это хорошо, но не всегда выходит получить решение, удовлетворяющее нас в плане скорости его работы, поэтому, если вы понимаете, что какой-то запрос проще написать с использованием SQL - лучше использовать его. Но, помните, что данный подходит может потребовать от вас переписать все запросы на другой диалект, в случае миграции на другую базу данных. Если же вы остаётесь на стороне XQuery, вашему вниманию несколько способов для оптимизации запросов.
Не пренебрегайте использованием Fields
Банально, но, в большинстве случаев, вам нужно лишь несколько столбцов из выборки - так зачем вытаскивать их все?
Пример:
for $elem in collaborators where $elem/is_dismiss = false() return $elem/Fields("id")
Не забывайте про $elem/__data
Если логика вашего программного кода выглядит таким образом, что после выборки вы сразу же вызываете в цикле множество OpenDoc(), используйте конструкцию return $elem/id, $elem/__data - это позволит оптимизировать последующие открытия документов.
Пример:
for $elem in documents where contains($elem/code, "faq_") return $elem/id, $elem/__data
Используйте $elem_qc в MatchSome
Данная возможность наиболее полезна в случаях, когда вы сталкиваетесь с необходимостью передать в MatchSome большое количество значений, например: идентификаторы участников какой-либо группы или мероприятия. При её использовании, данные передаются в запрос не напрямую, а через временную таблицу, что позволяет нам обойти ограничение на количество доступных элементов и ускоряет выполнение запроса.
Пример:
for $elem_qc in collaborators where MatchSome($elem_qc/position_parent_id, (6327975429225669221, 7059754636873218982)) return $elem_qc/Fields("id")
Финал
Надеюсь, вы узнали для себя что-то новое и у вас получится применять данные приёмы в работе - если же вы знаете о какой-то функциональности, не упомянутой в статье (например join, который не так давно добавили) - пиши об этом в комментариях.
И всегда помните, что это не тот XQuery, что используется в T-SQL.