При проектировании интеграций, разработке HTTP-сервисов или разборе логов обмена программисты часто сталкиваются с задачей: на входе есть только плоский строковый уникальный идентификатор (GUID или Ref_Key), и необходимо выяснить, какому именно объекту метаданных (справочнику, документу) он принадлежит. Ситуация усложняется тем, что в самой платформе 1С GUID не привязан жестко к типу данных на уровне структуры самого идентификатора. В этой статье мы подробно разберем, почему так происходит, и рассмотрим эффективные практические способы решения этой задачи.
Существует распространенное заблуждение, что в структуре GUID «зашит» код таблицы или тип объекта. Давайте проанализируем, как устроен уникальный идентификатор в 1С. Платформа генерирует GUID по стандартным алгоритмам UUID (обычно это UUID Version 1, базирующийся на времени генерации и MAC-адресе сетевой карты, либо случайный UUID Version 4).
Для оптимизации работы СУБД платформа выдает GUID сессиям небольшими пулами (обычно по 32 штуки) и записывает их в базу данных в «перевернутом» виде. Из-за этого объекты, созданные в одной сессии примерно в одно и то же время, имеют схожие идентификаторы. Это создает иллюзию наличия «системного кода типа» в средних байтах GUID, однако это не более чем совпадение временных меток. На самом деле, чистый строковый GUID — это просто 128-битное число. Без обращения к базе данных или метаданным определить тип объекта по нему невозможно.
Более того, правила платформы позволяют разработчикам принудительно устанавливать одинаковые GUID для объектов разных типов (например, с помощью метода УстановитьСсылкуНового()). Таким образом, в базе данных вполне могут мирно сосуществовать элемент справочника «Контрагенты» и документ «Реализация товаров и услуг» с абсолютно идентичными GUID. Это еще раз доказывает, что сам по себе идентификатор не определяет тип.
Тем не менее, решить задачу определения типа по GUID вполне возможно. Рассмотрим основные практические методы.
Чаще всего на практике количество возможных вариантов ограничено. Например, веб-сервис принимает Ref_Key, который может принадлежать только одному из трех документов: «Реализация товаров и услуг», «Поступление товаров и услуг» или «Заказ покупателя».
Вместо медленного перебора объектов через метод ПолучитьОбъект() или выполнения множества отдельных запросов к базе данных, мы рекомендуем составить один пакетный запрос с использованием конструкции ОБЪЕДИНИТЬ ВСЕ. Это позволит СУБД выполнить поиск максимально быстро за одно обращение.
При этом важно помнить о нюансе: мы не можем просто передать GUID в качестве параметра сравнения со ссылочным полем в запросе. Нам необходимо предварительно получить типизированную ссылку для каждого из проверяемых типов метаданных с помощью метода менеджера ПолучитьСсылку().
Давайте разберем пример кода, реализующий этот подход:
// Входной строковый GUID, полученный из внешней системы
СтрокаGUID = "80f40800-2771-598b-11e7-a3f0a3a64c3b";
УИД = Новый УникальныйИдентификатор(СтрокаGUID);
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ""РеализацияТоваровУслуг"" КАК ИмяТипа,
| Ссылка КАК Ссылка
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Док
|ГДЕ
| Ссылка = &СсылкаРеализация
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
| ""ПоступлениеТоваровУслуг"",
| Ссылка
|ИЗ
| Документ.ПоступлениеТоваровУслуг
|ГДЕ
| Ссылка = &СсылкаПоступление
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
| ""ЗаказПокупателя"",
| Ссылка
|ИЗ
| Документ.ЗаказПокупателя
|ГДЕ
| Ссылка = &СсылкаЗаказ";
// Формируем ссылки нужного типа с нашим GUID для передачи в параметры
Запрос.УстановитьПараметр("СсылкаРеализация", Документы.РеализацияТоваровУслуг.ПолучитьСсылку(УИД));
Запрос.УстановитьПараметр("СсылкаПоступление", Документы.ПоступлениеТоваровУслуг.ПолучитьСсылку(УИД));
Запрос.УстановитьПараметр("СсылкаЗаказ", Документы.ЗаказПокупателя.ПолучитьСсылку(УИД));
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
Сообщить("Объект найден! Тип метаданных: " + Выборка.ИмяТипа);
ИскомаяСсылка = Выборка.Ссылка;
Иначе
Сообщить("Объект с указанным GUID не найден среди проверяемых типов.");
КонецЕсли;
Этот метод чрезвычайно эффективен, поскольку СУБД ищет записи по первичному ключу (индексу), что занимает доли миллисекунды.
Если вы столкнулись с ситуацией, когда ссылка в базе данных «битая», платформа отображает ее в виде специальной строки, например: <Объект не найден> (26:80f408002771598b11e7a3f0a3a64c3b).
Здесь кроется важная подсказка:
26) — это шестнадцатеричный (HEX) код таблицы в базе данных SQL, в которой должен лежать объект.Зная шестнадцатеричный код таблицы (в нашем примере 26), мы можем сопоставить его с метаданными. Для этого выполним следующие шаги:
Шаг 1. Переведем HEX-код в десятичную систему счисления. Шестнадцатеричное 26 в десятичной системе равно 38 (2 * 16^1 + 6 * 16^0).
Шаг 2. Используем функцию структуры хранения базы данных. Метод ПолучитьСтруктуруХраненияБазыДанных() позволяет получить соответствие имен объектов метаданных и физических таблиц в SQL (таких как _Ref38 или _Document38). Если из сообщения о ненайденном объекте уже известен номер физической таблицы, связать её с метаданными можно через просмотр физических таблиц 1С. По одному GUID без кода таблицы этот инструмент тип объекта не определяет.
Давайте рассмотрим пример функции, которая выполняет такой поиск:
Функция ОпределитьИмяТаблицыПоКоду(ИскомыйКодДесятичный)
// Получаем структуру хранения для всей базы данных
ТаблицыБД = ПолучитьСтруктуруХраненияБазыДанных(, Истина);
// Ищем таблицу, чье физическое имя содержит наш индекс
// Например, для справочников это может быть "_Ref38", для документов — "_Document38"
Для Каждого СтрокаТаблицы Из ТаблицыБД Цикл
ИмяТаблицы = СтрокаТаблицы.ИмяТаблицыХранения;
Если Найти(ИмяТаблицы, "_" + Формат(ИскомыйКодДесятичный, "ЧГ=0")) > 0 Тогда
Возврат СтрокаТаблицы.ИмяТаблицы; // Возвращает имя метаданных, например, "Справочник.Номенклатура"
КонецЕсли;
КонецЦикла;
Возврат Неопределено;
КонецФункции
Важное предупреждение о производительности: Метод ПолучитьСтруктуруХраненияБазыДанных() выполняется довольно долго (на крупных базах данных процесс может занимать от нескольких секунд до минуты), так как платформа динамически опрашивает СУБД. Крайне не рекомендуется вызывать его «на лету» при каждом запросе к HTTP-сервису.
Если вы решите использовать этот подход, обязательно кэшируйте результаты сопоставления номеров таблиц и имен метаданных (например, сохраняйте их в специальный регистр сведений или в повторно используемый модуль). Поскольку структура таблиц меняется только в момент реструктуризации базы данных при обновлении конфигурации, этот кэш будет оставаться актуальным очень долго.
Если ваша конфигурация разработана на базе БСП (Библиотека Стандартных Подсистем), задача может быть решена еще изящнее. В БСП встроен специальный механизм регистрации объектов для синхронизации данных. Информация о внешних GUID сопоставляется с внутренними ссылками 1С в служебных регистрах сведений.
Проанализируйте состав вашей конфигурации на наличие следующих регистров:
РегистрСведений.ПубличныеИдентификаторыСинхронизируемыхОбъектов — предназначен для хранения связей между GUID объектов во внешней системе обмена и внутренними ссылками 1С.РегистрСведений.СоответствияОбъектовИнформационныхБаз — используется в традиционных планах обмена для сопоставления объектов между базами.Если ваши объекты проходят через механизмы синхронизации БСП, поиск по этим регистрам по измерению с типом УникальныйИдентификатор или строковому представлению GUID отработает мгновенно и сразу вернет готовую ссылку нужного типа.
Пытаться угадать тип данных по «голому» GUID — это следствие архитектурной ошибки при проектировании интеграционного решения. Чтобы избавить систему от лишней нагрузки и сделать обмен надежным, следуйте простым правилам:
Используйте составные ключи. Внешняя система при отправке запроса в 1С должна передавать не просто Ref_Key, а структуру, содержащую имя типа метаданных. Например:
{
"ObjectType": "Document_РеализацияТоваровУслуг",
"Ref_Key": "80f40800-2771-598b-11e7-a3f0a3a64c3b"
}
Следуйте стандартам OData. Если вы используете стандартный интерфейс OData 1С, помните, что сущности всегда разделены по типам ресурсов прямо в URL запроса (например, .../odata/standard.odata/Document_РеализацияТоваровУслуг(guid'...')). Платформа сама точно знает, к какой таблице обращаться, и вам не придется выполнять ручной поиск.