Перейти к основному содержимому

Связные и реляционные данные

Обычные fake-генераторы заполняют поля независимо, и получаются невозможные пары: марка Fiat с моделью Altima (это модель Nissan), город из одного региона с индексом из другого. TDC умеет иначе.

Приём простой: адрес шаблона может подставлять значение другого поля. Родитель называет файл, из которого берётся потомок:

value="common.vehicle.model.${{Brand}}"

Выпал бренд Fiat → адрес превращается в common.vehicle.model.Fiat, и модель берётся именно из файла Fiat. Никаких «Fiat Altima».

Вывод иллюстративен

Значения ниже получены с фиксированным seed, поэтому воспроизводимы, но точные строки и пропорции могут отличаться между версиями ядра. Считайте их примерами формы, а не гарантией.

Как это выглядит

Две последовательности: бренд и модель. Модель объявляет parent="Brand" (чтобы видеть выпавший бренд) и подставляет его в адрес template через ${{Brand}}:

<tdc>
<env count="5" seed="showroom" local="en">
<sequence name="Brand"><gen type="template" value="common.vehicle.brand"/></sequence>
<sequence name="Model" parent="Brand"><gen type="template" value="common.vehicle.model.${{Brand}}"/></sequence>
</env>
<block><line><data>${{Brand}} ${{Model}}</data></line></block>
</tdc>
./run showroom.tdc
Honda CR-V
Toyota Corolla
Ford Maverick
Chevrolet Bolt EV
Nissan Kicks

Каждая модель принадлежит своему бренду. Причём common.vehicle.brandвзвешенный пакет (Toyota частая, Maybach редкий), так что и марки выпадают в реальных пропорциях — вы получаете и связные пары, и правдоподобный срез рынка в одном конфиге.

Один потомок на родителя — кухня и её блюдо

Та же форма работает для любой пары «родитель/потомок». Кухня тянет своё блюдо (food.cuisinefood.dishByCuisine.<кухня>). Используйте это, когда два поля выглядели бы абсурдно, выпав независимо — «корейский фалафель» никого не убедит:

<sequence name="Cuisine"><gen type="template" value="food.cuisine"/></sequence>
<sequence name="Dish" parent="Cuisine"><gen type="template" value="food.dishByCuisine.${{Cuisine}}"/></sequence>
./run menu.tdc
Lebanese: Falafel
Korean: Bulgogi
Indian: Rogan Josh
Chinese: Peking Duck
Greek: Souvlaki

Один родитель, несколько связанных потомков

Один родитель может кормить сразу несколько потомков. Каждый потомок подставляет одно и то же значение родителя в свой адрес, так что все поля строки остаются согласованными между собой. Здесь страна (взвешенная по населению) тянет и столицу, и валюту:

<sequence name="Country"><gen type="template" value="geo.country"/></sequence>
<sequence name="Capital" parent="Country"><gen type="template" value="geo.capitalByCountry.${{Country}}"/></sequence>
<sequence name="Currency" parent="Country"><gen type="template" value="geo.currencyByCountry.${{Country}}"/></sequence>
./run atlas.tdc
China — Beijing — Renminbi
United States — Washington — US Dollar
India — New Delhi — Indian Rupee
Indonesia — Jakarta — Rupiah
China — Beijing — Renminbi

Используйте это, когда несколько полей зависят от одного ключа: части адреса, привязанные к региону; детали товара, привязанные к категории; данные подразделения, привязанные к отделу. Объявите каждого потомка с одним и тем же parent, и все они прочитают одно выбранное значение.

Как устроены данные

Родитель — обычный список; у каждого его значения есть свой файл-потомок, названный ровно этим значением:

data/packs/common/vehicle/
brand.txt # список брендов (родитель)
model/
Toyota.txt # модели Toyota
Fiat.txt # модели Fiat
Mercedes-Benz.txt # имена с дефисом и пробелом тоже работают

Адрес файла — путь через точку: model/Fiat.txtcommon.vehicle.model.Fiat. В шаблоне ${{Brand}} подставляет имя файла, и TDC находит нужный список. Чтобы добавить бренд, положите model/НовыйБренд.txt и допишите строку в brand.txt. Готовые связные наборы поставляются для марок авто, food.cuisine, medical.specialtyCoherent, work.industryCoherent, common.dev.languageCoherent, sport.sportCoherent и geo.country.

Что важно помнить

  • Родитель объявляется раньше потомка — TDC материализует последовательности сверху вниз, поэтому ${{Brand}} берёт уже посчитанное значение. Потомок, подставляющий поле, объявленное ниже себя, не найдёт что читать.
  • parent="Brand" связывает потомка с родителем и задаёт порядок. Для простой подстановки этого достаточно; более строгий отбор по конкретному значению (parent="Brand.Fiat") описан в Иерархических зависимостях.
  • У каждого значения родителя должен быть свой файл-потомок, иначе адрес не найдётся и будет ошибка. Поэтому список-родитель обычно содержит ровно те значения, для которых файлы есть (как common.vehicle.brand).
  • Движок. Такой конфиг всегда считает in-memory движок (единственный, кто умеет разрешать адрес по строке), поэтому память растёт вместе с count. Это про реалистичную связность, а не про потоковую генерацию гигабайтов; Какой движок считает ваш конфиг перечисляет и эту форму, и остальные четыре, которые уходят тем же путём.

CSV-родственник — row + weight

Когда связанные поля лежат в одном CSV, а не в отдельных файлах на каждое значение, свяжите их через row: несколько генераторов file с одинаковым row читают одну и ту же строку на запись, так что поля остаются в пределах одной строки настоящих данных. Добавьте weight к одному из них, чтобы тянуть строку по её реальной частоте:

<sequence name="Place">
<gen name="City" type="file" src="cities.csv" column="city" row="loc" weight="population"/>
<gen name="Region" type="file" src="cities.csv" column="region" row="loc"/>
</sequence>
./run cities.tdc
Москва, Московская обл.
Казань, Татарстан
Новосибирск, Новосибирская обл.
Москва, Московская обл.
Екатеринбург, Свердловская обл.

Поскольку оба генератора делят row="loc", город и его регион никогда не расходятся по разным записям; weight="population" у города делает так, что крупные пункты выпадают чаще. Подробности — в Генераторе file.

Смотрите также