Поделиться через


Связи сущностей

GraphQL запросы могут просматривать связанные объекты и их поля, поэтому с помощью одного запроса можно написать примерно следующее:

{
  books
  {
    items {
      id
      title    
      authors {
        items {
          first_name
          last_name
        }
      }
    }
  }
}

Для извлечения книг и их авторов.

Чтобы обеспечить эту возможность, построитель API данных должен знать, как два объекта связаны друг с другом. Раздел relationships в файле конфигурации содержит необходимые метаданные для правильной и эффективной работы этой возможности.

Настройка связи

Независимо от того, какую базу данных вы используете с построителем API данных, необходимо явно сообщить построителю API данных, что объект связан с другим. Существует три типа связей, которые можно установить между двумя сущностями:

Связи «один ко многим»

Связь "один ко многим" позволяет объекту получить доступ к списку связанных объектов. Например, серия книг может разрешить доступ ко всем книгам этой серии:

{
  series {
    items {
      name
      books {
        items {
          title
        }
      }
    }
  }
}

Если есть внешние ключи, поддерживающие связь между двумя базовыми объектами базы данных, необходимо только сообщить конструктору API данных, что вы хотите предоставить такую связь. С помощью ИНТЕРФЕЙСА КОМАНДНОй строки DAB:

dab update Series --relationship books --target.entity Book --cardinality many 

Обновление сущности series , используемое в примере:

"Series": {
  "source": "dbo.series",
  ...
  "relationships": {
    "books": {
      "target.entity": "Book",
      "cardinality": "many"    
    }
  }
  ...
}

В элементе relationships добавляется новый ключ: books. Элемент определяет имя, которое используется для поля GraphQL для перехода от series объекта к объекту, определенному target.entityв , Book в данном случае. Это означает, что в файле конфигурации должна быть сущность с именем Book .

Свойство cardinality сообщает построителю API данных, что в каждой серии может быть много книг, поэтому созданное поле GraphQL возвращает список элементов.

Это свойство все, что вам нужно. При запуске построитель API данных автоматически обнаруживает поля базы данных, которые необходимо использовать для поддержания определенной связи.

Если у вас нет ограничения внешнего ключа, поддерживающего связь с базой данных, построитель API данных не сможет автоматически определить, какие поля используются. Чтобы сообщить построителю API данных, какие поля связаны с двумя сущностями, необходимо указать их вручную. Их можно указать с помощью ИНТЕРФЕЙСА командной строки с помощью dab update:

dab update Series --relationship books --target.entity Book --cardinality many  --relationship.fields "id:series_id"

Параметр relationship.fields позволяет определить, какие поля используются из обновляемой сущности (Series), а какие — из целевой сущности (Book) для подключения данных из одной сущности к другой.

В предыдущем примере id поле базы данных сущности Series сопоставляется с полем series_id базы данных сущности Book .

Конфигурация также содержит следующие сведения:

"Series": {
  "source": "dbo.series",
  ...
  "relationships": {
    "books": {
      "cardinality": "many",
      "target.entity": "Book",
      "source.fields": ["id"],
      "target.fields": ["series_id"]
    }    
  }
  ...
}

Связь "многие к одному"

Связь "многие ко многим" похожа на связь "один ко многим" с двумя основными отличиями:

  • для cardinality задано значение one
  • Созданное поле GraphQL возвращает скаляр, а не список

После примеров серии книг, использовавшихся ранее, книга может находиться только в одной серии, поэтому связь создается с помощью следующей команды ИНТЕРФЕЙСА КОМАНДНОй строки DAB:

dab update Book --relationship series --target.entity Series --cardinality one

Которая создает эту конфигурацию:

"Book": {
  "source": "dbo.books",
  ...
  "relationships": {       
    "series": {
      "target.entity": "Series",
      "cardinality": "one"
    }
  }
}

Что, в свою очередь, позволяет GraphQL запрос, как в следующем примере:

{
  books {
    items {
      id
      title    
      series {
        name
      }
    }
  }
}

Где каждая книга возвращает также ряд, к которому она принадлежит.

Связь "многие ко многим"

Отношения "многие ко многим" можно рассматривать как пару связей "один ко многим" и "многие ко многим", работающих вместе. Автор, безусловно, может написать несколько книг (связь "один ко многим"), но также верно, что несколько авторов могут работать над одной книгой (связь "многие ко одному").

Построитель API данных поддерживает этот тип связи в собственном коде:

  • Использование пары связей "один ко многим" и "многие к одному".
  • Использование связывающего объекта.

Использование пары связей "один ко многим" и "многие к одному"

Одним из бизнес-требований, которое, скорее всего, будет отслеживание того, как роялти разделены между авторами книги. Для реализации такого требования требуется выделенная сущность, которая связывает автора, книга и назначенные роялти. Поэтому требуются три сущности:

  • authors— для представления биографических сведений об авторах.
  • books, чтобы представлять данные книги, такие как название и международный стандартный номер книги (ISBN).
  • books_authors для представления данных, связанных как с книгой, так и с ее автором, например, процент роялти, который автор получает за определенную книгу.

Эти три сущности можно визуализировать на следующей схеме.

Схема, показывающая отношения

Как видно, существует две двунаправленные связи:

  • Связь "один ко многим"/ "многие к одному" между authors и books_authors
  • Связь "один ко многим"/ "многие к одному" между books и books_authors

Для корректной обработки такого сценария с помощью DAB достаточно создать связанные сущности и сопоставления в файле конфигурации. Предположим, Book что сущность и Author уже находятся в файле конфигурации:

dab add BookAuthor --source dbo.books_authors --permissions "anonymous:*"

Чтобы добавить новую сущность, выполните команду dab update:

dab update Book --relationship authors --target.entity BookAuthor --cardinality many --relationship.fields "id:book_id"
dab update Author --relationship books --target.entity BookAuthor --cardinality many --relationship.fields "id:author_id"

Чтобы добавить связи к созданной BookAuthor сущности, выполните dab update еще раз:

dab update BookAuthor --relationship book --target.entity Book --cardinality one --relationship.fields "book_id:id"
dab update BookAuthor --relationship author --target.entity Author --cardinality one --relationship.fields "author_id:id"

Добавление связей из BookAuthor сущностей Book и Author . С помощью предоставленной конфигурации DAB может обрабатывать вложенные запросы, как в следующем примере:

{
 authors {
    items {
      first_name
      last_name      
      books {
        items {
          book {
            id
            title
          }
          royalties_percentage
        }
      }      
    }
  }
}

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

Использование связывающего объекта

Процесс, описанный в предыдущем разделе, отлично подходит для доступа ко всем сущностям, участвующим в связях "многие ко многим", через GraphQL. Этот сценарий не всегда так. Например, если вам не нужно отслеживать роялти, BookAuthor сущность на самом деле не приносит никакой ценности для конечного пользователя. Сущность использовалась только для связанных книг с авторами. В реляционных базах данных связи "многие ко многим" создаются с помощью такой третьей таблицы, которая связывает таблицы, участвующие в связи "многие ко многим":

Схема, показывающая еще одну связь

На схеме видно, что есть таблица с именем books_authors , которая связывает авторов с их книгами, а книги с их авторами. Эта связываемая таблица не требуется предоставлять пользователю. Связывая таблица — это просто артефакт, позволяющий существовать связи "многие ко многим", но построитель API данных должен знать о ее существовании, чтобы правильно использовать ее.

ИНТЕРФЕЙС КОМАНДНОй строки DAB можно использовать для создания связи "многие ко многим", а также настройки объекта связывания (удалите все связи, созданные в предыдущем разделе, и начинайте только с Book сущности и Author без настроенной связи между ними).

dab update Book --relationship authors --target.entity Author --cardinality many --relationship.fields "id:id" --linking.object "dbo.books_authors" --linking.source.fields "book_id" --linking.target.fields "author_id" 

При этом файл конфигурации JSON обновляется так, как в следующем примере:

"Book": {
  "source": "dbo.books",
  ...
  "relationships": {       
    "authors": {
      "cardinality": "many",
      "target.entity": "author",
      "source.fields": [ "id" ],
      "target.fields": [ "id" ],
      "linking.object": "dbo.books_authors",
      "linking.source.fields": [ "book_id" ],
      "linking.target.fields": [ "author_id" ]
    }
  }
}

Конфигурация сообщает DAB, что вы хотите добавить authors поле в Book сущность, которая разрешает доступ авторам книги. authorsможет иметь значение many, поэтому список авторов возвращается, когда запрос GraphQL обращается к полюauthors. Эта связь определяет способ перехода междукнигами и авторами: поля базы данных, используемые для перехода между книгами и их авторами, определяются в source.fields для книги и в target.fields для авторов аналогично связи "один ко многим" или "многие к одному", описанной ранее в этой статье.

Это отношение "многие ко многим", поэтому между двумя сущностями нет прямого соединения, поэтому linking.object необходимо использовать . В примере таблица dbo.books_authors базы данных используется в качестве связывающего объекта. То, как объект ссылки может подключать книги к авторам, определяется в свойствах linking.source.fields и linking.target.fields . Первый из них сообщает DAB о том, как исходная сущность — Book — подключена к объекту liking, а вторая — о том, как связанный объект подключен к целевой Author сущности в примере.

Чтобы понять, как используются предоставленные сведения, можно использовать следующий пример эквивалентного запроса:

select * 
from dbo.books as b
inner join dbo.books_authors as ba on b.id = ba.book_id 
inner join dbo.authors a on ba.author_id = a.id 

С помощью предоставленной конфигурации DAB может понять GraphQL, как в следующем примере:

{
  books {
    items {
      id
      title
      authors {
        items {
          first_name
          last_name
        }
      }
    }
  }
}

Где вы хотите получить книги и их авторов.

Чтобы разрешить переход от Author к Book, можно применить те же принципы, обновив конфигурацию с помощью следующей команды:

dab update Author --relationship books --target.entity Book --cardinality many --relationship.fields "id:id" --linking.object "dbo.books_authors" --linking.source.fields "author_id" --linking.target.fields "book_id" 

Определяет связь "многие ко многим" между сущностью Author и сущностью Book с использованием связующего объекта dbo.books_authors в фоновом режиме.