Свяжите базу данных со схемой, и Entity Enricher будет поддерживать ваши обогащённые сущности в виде реляционных таблиц, которыми владеете вы: настоящая таблица company со столбцом revenue — а не файл экспорта. Скачайте готовый к запуску SQL-снимок один раз, а затем поддерживайте базу данных в согласованном состоянии с помощью инкрементального потока дельт из идемпотентных upsert-операций.
Сохраняйте один раз, проецируйте по требованию. Обогащения записывают текущее состояние сущности в слой сущностей; каждая связанная база данных — это проекция этого состояния, поставляемая в виде разового снимка и потока дельт, который вы подтверждаете. Изменение схемы обходится в повторную проекцию, но никогда не требует миграции данных.
database_sync: false); enrichment других пользователей продолжают маршрутизироваться в обычном режиме..sql (таблицы + данные) и примените его к вашей PostgreSQL. DDL поставляется с индексами для соединений и внешними ключами (дочерние и связующие строки удаляются каскадно при удалении entity). В заголовке файла указано, с какого дельта-курсора возобновить.Каждый тип сущности имеет ключ базы данных — набор столбцов, который ваши таблицы используют как уникальный индекс и как цель конфликта при upsert. При подключении базы данных проход ИИ-классификации предлагает вам на проверку всю модель базы данных (ключи, типы столбцов, индексы, владение), опираясь на простую цепочку: семантический ID объекта, если он есть, иначе поле вида Id (id, product_id, …), иначе естественные ключи объекта. В ваших таблицах семантический ID появляется как столбец semantic_id — имя id остаётся свободным для ваших нужд. Изменить их можно в любой момент на вкладке «Модель» базы данных — после публикации это изменение уровня миграции: затем заново скачайте снимок. Ничего не попадёт в вашу базу данных, пока схема не опубликована с этой вкладки (само по себе подключение не создаёт ни таблиц, ни строк).
Ключ базы данных никогда не допускает null. Значение null не соответствует ни одной строке, поэтому вместо обновления сущности при каждом обогащении вставлялся бы новый дубликат — именно поэтому обогащение, ключ которого возвращается пустым, отклоняется, а не сохраняется. Редактор разделяет эти два флага, и схема, задающая оба, отклоняется при сохранении или привязке базы данных, с указанием двух способов это исправить: сделать свойство всегда присутствующим или назначить ключом типа другое свойство.
Если ключ базы данных — это обогащённое текстовое поле, а не семантический ID, сохранённым значением будет ответ модели, а не отправленный вами текст. Указание компании как Embraer в запросе не фиксирует это поле: обогащение может вернуть Embraer S.A. — исправленное написание, развёрнутый юридический суффикс, отброшенное уточнение, — и именно это значение станет ключом строки. Поэтому поиск строки по отправленному вами значению может её не найти (используйте entity_keys, возвращаемые с каждым сохранённым обогащением), а более поздний запуск с другой формулировкой даст другой ключ — и вставит вторую строку вместо обновления вашей. Так ведёт себя любой ключ, построенный на обогащённом тексте. Решение — семантический ID для этого типа: варианты одного названия сводятся к единой стабильной идентичности, и строка сохраняется независимо от того, как её напишет модель. Включите его при генерации схемы — добавление позже означает правку каждого объекта.
Объекты, вложенные в массивы, становятся отдельными таблицами со связующими строками, сохраняющими порядок; объекты-значения без идентичности остаются встроенными в столбцы родителя. Имена свойств используются дословно (в кавычках) — имена ваших столбцов совпадают с именами свойств вашей схемы и сокращаются только там, где вложенный путь превысил бы допустимую в PostgreSQL длину имени.
Проекция детерминирована — одна и та же схема всегда сопоставляется с одними и теми же таблицами и столбцами. Имена свойств становятся именами столбцов дословно (в кавычках); имена типов преобразуются в snake_case и становятся именами таблиц (VideoGame → video_game). Единственное исключение — длина: PostgreSQL не может хранить идентификатор длиннее 63 байт, поэтому глубоко вложенный путь сокращает свои родительские объекты, чтобы уместиться (morphological_description_ → morpdesc_) — один и тот же префикс для каждого столбца этого объекта, отображаемый и редактируемый во вкладке «Модель» перед связыванием. Свойство также может полностью отбросить свой префикс, чтобы соответствовать столбцу, который уже есть в вашей базе данных (product_identifiers.stock_keeping_unit → sku) — переключатель связывания рядом с именем во вкладке «Модель». Каждая таблица содержит столбец _sync_revision, используемый для обеспечения сходимости повторов.
| В вашей схеме | В вашей базе данных |
|---|---|
| Объект с идентичностью (семантический ID или ключи) | Собственная таблица; ключи базы данных становятся уникальным индексом и целью upsert |
| Скалярное поле (string, number, boolean) | Типизированный столбец (TEXT, BIGINT, NUMERIC, BOOLEAN) |
| Закрытый набор (поле, ограниченное списком значений) | Обычный столбец TEXT — без CHECK, без типа enum в базе данных. Список проверяется в момент ответа ИИ, поэтому добавление в него нового значения никогда не приводит к миграции вашей базы данных. При желании добавьте собственное ограничение — синхронизация его не затрагивает |
| Поле, допускающее или не допускающее NULL | Поле, не допускающее NULL, по умолчанию становится колонкой NOT NULL — в связке с контролем качества ниже: при самом строгом контроле обязательные ссылки получают и внешние ключи NOT NULL; отключите это ограничение — при регистрации или позже: изменение после первой синхронизации попадает в ленту как защищённая миграция, — чтобы все колонки остались nullable и полноту данных проверял только контроль качества |
| Многоязычное поле | Один столбец JSONB, содержащий все языки |
| Встроенный объект-значение (без идентичности) | Разворачивается в столбцы с префиксом (dimensions_width) |
| Массив объектов-значений | Дочерняя таблица с ключом на родителя, упорядоченная, с каскадным удалением |
Массив сущностей / связь $ref | Связующая таблица, соединяющая исходные и целевые строки, с сохранением порядка |
Ключевое поле (identifying) | Вторичный индекс для быстрого поиска |
| Индекс под запрос (упорядоченный список полей) | По одному многоколоночному индексу на каждую объявленную форму, с порядком колонок как в запросе экрана списка, который он обслуживает: сначала фасеты и закрытые наборы, в конце — колонка сортировки или диапазона, включая многоязычные поля (такая форма создаётся по одной на каждый язык, полученный вашей базой данных); предлагается на этапе классификации с обоснованием, настраивается на вкладке «Модель», по нескольку на сущность |
| Поле поиска (назначение индекса) | Триграммный индекс (pg_trgm) по тексту, в котором ваши поля поиска ищут по фрагменту, — отдельно для каждого языка и без ограничения длины на многоязычных колонках; никогда не на значении выпадающего списка — ему место в индексе под форму запроса (включая многоязычные: такой индекс создаётся по одному на каждый язык, полученный вашей базой данных); на репликах без расширения пропускается (ничего не делает), пока владелец базы данных его не установит |
| Пара координат (широта + долгота) | Один пространственный индекс по паре (встроенный PostgreSQL GiST, без расширений) — запросы по радиусу, по ближайшим соседям и по видимой области карты |
| Пара интервала (начальная + конечная границы) | Один диапазонный индекс по паре — запросы на пересечение и «какое значение действовало на эту дату» |
База данных может синхронизировать более одной схемы. Типы сущностей с одинаковым именем в связанных схемах попадают в одну и ту же таблицу, объединяясь по ключу базы данных — обогащения каждой схемы обновляют только свои столбцы, поэтому компания, обогащённая двумя схемами, становится одной строкой, несущей оба набора столбцов. Типы, уникальные для схемы, просто добавляют собственные таблицы, доставляемые через автоматическую дельту миграции в фиде — повторная загрузка не требуется.
Когда вы связываете схему, шаг сравнения показывает, какие именно таблицы будут объединены (с их ключами и добавленными столбцами), а какие являются новыми; схема, использующая общую таблицу, принимает существующие ключи базы данных этой таблицы, показанные для вашего ознакомления. Если у схем нет ничего общего, процесс предлагает выделенную базу данных. Отвязка схемы никогда не затрагивает вашу базу данных — синхронизированные таблицы остаются.
Отвязка схемы или удаление синхронизации оставляет на нашей стороне две вещи: сохранённое состояние сущностей, в которое больше ничего не пишет, и свойства базы данных, которые несёт схема (ключи базы данных, типы столбцов, индексы, принадлежность). Оба подтверждения предлагают их удалить — и только для схем, оставшихся вообще без базы данных: если схема ещё где-то синхронизируется, всё сохраняется. Сама схема, её записи обогащения и расходы никогда не затрагиваются.
У связанной схемы есть опубликованный контракт — версия, которую фактически используют ваши обогащения и ваша база данных. Редактирование схемы затрагивает только рабочую копию: изменения формулировок применяются автоматически, а структурные изменения (новые поля, изменения типов или ключей) ждут, пока вы не нажмёте Опубликовать. При публикации показывается точное влияние изменений и в поток дельт отправляется нужная миграция: новые столбцы приходят как дельты ALTER TABLE, а более серьёзные изменения (новый ключ базы данных, изменение типа) выполняются как защищённые миграции против вашей собственной базы данных — если данные им мешают (отсутствующее или дублирующееся значение ключа), поток приостанавливается с точным описанием проблемы и автоматически повторяет попытку после того, как вы её исправите.
Публикация находится на вкладке Model базы данных (пока schema связана, Workflow Editor показывает баннер с указанием на неё). Перед публикацией показываются обе стороны: контракт, на котором ваша база данных находится сегодня, и различия всего, что изменит ваша рабочая копия. Не убеждены в правке? Вернуться к опубликованной версии восстанавливает контракт — это можно отменить, а отложенный черновик остаётся восстановимым в течение 24 часов.
Повторная привязка схемы, отредактированной в отвязанном состоянии, работает так же: синхронизация помнит, что уже есть в вашей базе данных, и отправляет только различия, плюс обновление снимка для строк, записанных за это время. Никакого ручного DROP, никогда.
То же обязательство распространяется и на наши собственные обновления. Когда новая версия улучшает сопоставление schema с таблицами, ваша синхронизация переносится автоматически — дополняющие изменения приходят в подачу данных сами по себе. Если же обновление изменит структуру уже имеющихся у вас таблиц, мы никогда не трогаем ваши данные без предупреждения: доставка приостанавливается, и страница Database Sync предлагает вам применить обновление, заранее показывая, что именно изменится.
Многоязычное обогащение здесь тоже полноценно поддерживается: локализованные значения поступают в виде столбцов JSONB, содержащих все языки обогащения — {"en": "Headache", "fr": "Céphalée"} — так что одна база данных сразу обслуживает все ваши локали. Выбирайте язык прямо в своих запросах (name->>'fr'), а полезные нагрузки дельт в JSON содержат те же объекты с ключами по языкам.
Каждая база данных при регистрации отвечает на один вопрос: что записывать, если обогащение вернулось с пробелами — незаполненными полями, не допускающими NULL? Три ответа образуют лестницу. Ничего: один пробел в любом месте, в том числе внутри вложенного объекта, — и сущность отклоняется. Сущность без её неполных дочерних объектов (по умолчанию): собственная строка сущности должна быть полной, но сломанный дочерний объект пропускается и попадает в отчёт, вместо того чтобы обрушить всё обогащение. Всё: пробелы записываются как NULL, и ничего не отклоняется — но состояние сущности определяется по принципу «побеждает последняя запись», последнее обогащение и есть строка, поэтому более поздний неполный прогон стирает то, что заполнил предыдущий. Именно ради предотвращения такого стирания и существуют две строгие ступени.
Обогащения, не прошедшие проверку, всё равно сохраняются как записи и всё равно вызывают вебхук record.created — со значением database.saved = false — и сообщают, каких именно обязательных полей не хватило, так что неполные данные никогда не исчезают незаметно. По каждому отсутствующему полю также указано, объявила ли модель значение неизвестным или просто его пропустила: в первом случае нужна более мощная модель, веб-поиск или исходный документ, во втором — стоит проверить схему или входные данные. Всегда обязательны только поля ключа базы данных: обогащение без значения ключа отклоняется на любом уровне.
На строгих ступенях флажок переносит тот же контракт в вашу собственную базу данных в виде колонок NOT NULL для каждого всегда присутствующего поля. На самой строгой ступени ограничения накладываются и на внешние ключи обязательных ссылок — ни одна принятая строка не может обойтись без них. В режиме пропускать неполные дочерние объекты они намеренно остаются nullable: элемент списка, которому не хватает собственного значения, удаляется, а общая ссылка «один к одному» с неполной целевой сущностью (стадион, год открытия которого никому не известен) отсоединяется — эта цель не записывается и не обновляется, а сохранённая строка ни на что там не ссылается, из-за чего именно в эти колонки внешних ключей записывается NULL. И то и другое отражается в ответе на обогащение, а пробелы в полях верхнего уровня всегда приводят к отклонению. Изменение политики после первой синхронизации никогда не пропадает даром: оно попадает в ленту как защищённая миграция, проверенная на строках, которые уже хранит ваша база данных.
Вторая проверка отлавливает дублирующиеся идентичности: когда два элемента одного списка сводятся к одному и тому же ключу базы данных — модель придумала один id для двух разных компаний или ключ их не различает — строка может быть только одна, поэтому записывается последняя, а предыдущие отбрасываются: то же правило «побеждает последняя запись», что и везде. Каждое столкновение фиксируется с идентифицирующими значениями обоих элементов и вердиктом: дубликат повторил те же значения и ничего не потерял, конфликтующее отбрасывание потеряло названные им значения — либо модель повторила один объект с расхождениями в значениях, либо это разные объекты и ключу нужно различающее свойство (регион, год, версия). Список сохраняется в записи, поэтому частичная запись расскажет о потерянном ещё долго после того, как сам ответ исчезнет.
Одно замечание, прежде чем вы запустите повторное обогащение: для списка, принадлежащего родительской записи, список последнего обогащения и есть список. Дочерняя строка, которую не повторяет самый свежий ответ, удаляется из вашей базы данных — именно так до вас доходит настоящее удаление, и ответ об этом не сообщает. Это важно, когда список — тот, который модель вспоминает, а не перечисляет: спросите дважды об изотопах элемента или наградах человека, и второй ответ может оказаться короче, из-за чего удалятся строки, которые были верны. Ведите собственную историю, если вам нужно объединение всех запусков.
Обогащения попадают в связанную базу данных сами. Всё остальное — результат, отклонённый шлюзом до того, как вы исправили схему, намеренно исключённый запуск или результат, который вы хотите сначала просмотреть или исправить, — проходит через отправку записей в базу данных: выберите их на странице «История» или вызовите API из рабочего процесса.
Полный цикл. Обогатите данные с отключённым Database Sync, преобразуйте или одобрите результат в своём рабочем процессе, а затем отправьте его. Отправленное повторно проверяется по опубликованному контракту схемы и проходит тот же шлюз допуска, что и обогащение, — внедрение никогда не запишет то, что не смогло бы записать обогащение.
Две детали, о которых стоит знать. Если отправить запись без изменений, она сохраняется под этой записью. Отправка изменённого результата создаёт новую запись со ссылкой на исходную, потому что записи — это журнал аудита: они никогда не меняются под данными, которые на них ссылаются, поэтому содержимое вашей базы данных всегда прослеживается до записи ровно с этими значениями. А проверка использует контракт в том виде, в каком он существует сегодня: если схема изменилась с момента создания записи, страница «История» предупредит об этом до отправки.
На странице «История» также видно по каждой записи, дошла ли она до базы данных: отправлена, отправлена частично или отклонена с указанием причины. Доступно в веб-приложении, через API, MCP, n8n и Make.
Лента — это строгая FIFO-очередь для каждой базы данных: получите окно (при желании с арендой, чтобы пакет упавшего обработчика был доставлен повторно раньше более новых данных), примените изменения, подтвердите. Webhook-уведомления объединяются — каждая новая дельта сбрасывает таймер периода затишья, поэтому серия обогащений анонсируется один раз; настраиваемая максимальная задержка ограничивает ожидание, а полностью заполненная страница выборки отправляется немедленно. Два параметра очистки определяют, что хранит Entity Enricher: удалять доставленные копии дельт при подтверждении и — для минимизации данных — удалять само состояние сущности, как только его получили все базы данных, связанные со схемой. Для каждого можно задать необязательную задержку в днях: доставленные копии сохраняются в течение этого срока после подтверждения (окно для повторной доставки), а доставленная сущность хранится, пока не пробудет столько времени без обновлений; ежечасная очистка удаляет всё просроченное. Учтите, что очистка состояния — это минимизация, а не стирание: записи обогащения сохраняются, пока вы их не удалите, а для очищенных сущностей отключается объединение данных между обогащениями.
Эндпоинт контрольной суммы для каждой таблицы позволяет в любой момент проверить, что ваша реплика синхронизирована, без повторной загрузки.
Всё собрано в одном месте приложения — Database Sync, сразу под «Историей» в боковой панели: зарегистрировать базу данных для любой схемы (с проверкой ключа базы данных), связать или отвязать схемы, приостановить переключателем поток обогащения связанной схемы (никаких новых данных и уведомлений до повторного включения — публикации схем по-прежнему передают свой DDL, а обогащения, выполненные во время паузы, попадут в реплику только через повторную загрузку снимка), изменить её параметры, посмотреть эндпоинт вебхука и раскрыть его ключ подписи, скачать снимок, просмотреть текущее состояние сущностей, изучить очередь ожидающих дельт (только для чтения — курсор вашего рабочего процесса не затрагивается) и открыть диаграмму «сущность — связь» сгенерированных таблиц с их ключами и связующими таблицами. При синхронизации с несколькими базами данных диаграмма может сфокусироваться на одной схеме: таблицы, столбцы и связи, поставляемые другими схемами, становятся серыми — оставаясь при этом на своих местах, — так что вы точно видите, что каждая схема добавляет в общие таблицы.
Когда несколько баз данных оказываются на одной машине, кнопка панели инструментов Хосты синхронизации избавляет от процедуры сопряжения для каждой базы: выполните сопряжение машины один раз, а затем назначайте ей регистрации. Хост забирает каждую из них, создаёт физическую базу данных, если её нет, и начинает синхронизацию — так регистрация базы данных становится решением, которое вы принимаете здесь, а не сеансом терминала на сервере. Сопряжение выполняется для каждого сервера Entity Enricher, поэтому одна машина может параллельно обслуживать несколько экземпляров.
Запрос, который отвергает ваша база данных — обычная причина в том, что под новым уникальным индексом уже есть дубликаты, — не задерживает стоящую за ним очередь. Пакет этого обогащения уходит в карантин, поток продолжает идти, а сам пакет появляется на вкладке Карантин вместе с запросом, который отклонила ваша база данных. Устраните причину и выполните повторную отправку — она заново проецирует сущность из её текущего состояния, а не повторяет устаревший запрос, — либо отбросьте пакет.
GET /api/databases//changes?since=…&format=sql, затем POST /api/databases//ack.Работаете с Supabase? Наше сравнение с Supabase MCP показывает на примерах JSON и небольших схемах таблиц, как правила связей и синхронизации EE защищают каталог товаров.
Ничто в синхронизации не выполняется на сервере вашей базы данных — каждый из описанных выше вариантов это исходящий потребитель, подключающийся к любому указанному вами DSN. Azure Database for PostgreSQL, OVHcloud, AWS RDS, Supabase или любой другой управляемый экземпляр работает точно так же, как self-hosted: направьте потребителя на облачный DSN (управляемые провайдеры обычно требуют TLS, поэтому добавьте sslmode=require) и примените.
--dsn на управляемый экземпляр.delta_available будит функцию Azure Function / AWS Lambda / OVHcloud, которая забирает REST-поток, выполняет SQL и подтверждает.Два правила обеспечивают безопасность любого самописного потребителя: выполняйте инструкции каждого пакета по порядку, в рамках одной транзакции и подтверждайте только после коммита. Дельты идемпотентны и защищены ревизиями, поэтому сбой до подтверждения просто означает, что пакет будет доставлен повторно, а повторное применение сходится.
Базы данных доступны на платных тарифах (тариф определяет, сколько их можно зарегистрировать). PostgreSQL — стартовый диалект; каждая база данных объявляет свой диалект, поддержка MySQL / MariaDB, SQL Server и Oracle запланирована.