Compartilhar via


Relações de entidade

GraphQL consultas podem percorrer objetos relacionados e seus campos, para que com apenas uma consulta você possa escrever algo como:

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

Para recuperar livros e seus autores.

Para permitir que essa capacidade funcione, o Construtor de API de Dados precisa saber como os dois objetos estão relacionados entre si. A relationships seção no arquivo de configuração fornece os metadados necessários para fazer essa capacidade funcionar de forma correta e eficiente.

Configurando uma relação

Não importa qual banco de dados você esteja usando com o Construtor de API de Dados, você precisa informar explicitamente ao Construtor de API de Dados que um objeto está relacionado a outro. Há três tipos de relações que podem ser estabelecidas entre duas entidades:

Relação um para muitos

Uma relação um-para-muitos permite que um objeto acesse uma lista de objetos relacionados. Por exemplo, uma série de livros pode permitir o acesso a todos os livros dessa série:

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

Se houver Chaves Estrangeiras que dão suporte à relação entre os dois objetos de banco de dados subjacentes, você só precisará informar ao Construtor de API de Dados que deseja expor essa relação. Com a CLI do DAB:

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

O que atualiza a series entidade – usada no exemplo:

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

Uma nova chave é adicionada sob o relationships elemento : books. O elemento define o nome usado para o campo GraphQL navegar do series objeto para o objeto definido no target.entity, Book nesse caso. Isso significa que deve haver uma entidade chamada Book no arquivo de configuração.

A cardinality propriedade informa ao Construtor de API de Dados que pode haver muitos livros em cada série, portanto, o campo GraphQL criado retorna uma lista de itens.

Essa propriedade é tudo o que você precisa. Na inicialização, o Construtor de API de Dados detecta automaticamente os campos de banco de dados que precisam ser usados para sustentar a relação definida.

Se você não tiver uma restrição foreign key que sustenta a relação de banco de dados, o construtor de API de Dados não poderá descobrir automaticamente quais campos são usados. Para informar ao Construtor de API de Dados quais campos relacionam as duas entidades, especifique-as manualmente. Você pode especificá-los com a CLI usando dab update:

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

A opção relationship.fields permite definir quais campos são usados da entidade que está sendo atualizada (Series) e quais campos são usados da entidade de destino (Book), para conectar os dados de uma entidade à outra.

No exemplo anterior, o id campo de banco de dados da Series entidade é correspondido com o campo series_id de banco de dados da Book entidade.

A configuração também contém essas informações:

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

Relação muitos para um

Uma relação muitos para um é semelhante à relação um-para-muitos com duas diferenças principais:

  • o cardinality está definido como one
  • o campo GraphQL criado retorna um escalar e não uma lista

Seguindo os exemplos de Série de Livros usados antes, um livro pode estar em apenas uma série, portanto, a relação é criada usando o seguinte comando da CLI do DAB:

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

O que gera essa configuração:

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

O que, por sua vez, permite uma consulta GraphQL como este exemplo:

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

Onde cada livro retorna também a série à qual pertence.

Relação muitos para muitos

Muitos para muitos relacionamentos podem ser vistos como um par de relações um-para-muitos e muitos-para-um trabalhando juntos. Um autor certamente pode escrever mais de um livro (uma relação Um-para-Muitos), mas também é verdade que mais de um autor pode trabalhar no mesmo livro (uma relação Muitos para Um).

O construtor de API de Dados dá suporte a esse tipo de relação nativamente:

  • Usando um par de relações Um para Muitos/Muitos para Um.
  • Usando um objeto de vinculação.

Usando um par de relações um para muitos/muitos para um

Um requisito comercial que provavelmente estará lá é acompanhar como os royalties são divididos entre os autores de um livro. Para implementar esse requisito, uma entidade dedicada que vincula um autor, um livro e os royalties atribuídos são necessários. Portanto, são necessárias três entidades:

  • authors, para representar detalhes biográficos dos autores.
  • books, para representar dados do livro, como título e NÚMERO do Livro Padrão Internacional (ISBN).
  • books_authors para representar dados relacionados a um livro e ao seu autor, por exemplo, o percentual de royalties que um autor obtém para um livro específico.

As três entidades podem ser visualizadas por meio do diagrama a seguir.

Diagrama mostrando a relação muitos para muitos entre autores, books_authors e livros.

Como visível, há duas relações bidirecionais:

  • Relação um para muitos/muitos para um entre authors e o books_authors
  • Relação um para muitos/muitos para um entre books e o books_authors

Para lidar com esse cenário normalmente com o DAB, tudo o que é necessário é criar as entidades e mapeamentos relacionados no arquivo de configuração. Supondo que a Book entidade e Author já estejam no arquivo de configuração:

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

Para adicionar a nova entidade, execute 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"

Para adicionar as relações à entidade recém-criada BookAuthor , execute dab update novamente:

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"

Para adicionar as relações de BookAuthor às Book entidades e Author . Com a configuração fornecida, o DAB é capaz de lidar com consultas aninhadas como este exemplo:

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

Onde você está pedindo para devolver todos os autores, o livro que eles escreveram junto com os royalties relacionados.

Usando um objeto de vinculação

O processo descrito na seção anterior funciona muito bem se todas as entidades envolvidas nas relações Muitos para Muitos precisarem ser acessadas por meio de GraphQL. Esse cenário nem sempre é o caso. Por exemplo, se você não precisar controlar os royalties, a BookAuthor entidade não trará nenhum valor para o usuário final. A entidade só foi usada para associar livros a seus autores. Em bancos de dados relacionais, relações muitos para muitos são criadas usando essa terceira tabela que vincula as tabelas que participam da relação Muitos para Muitos:

Diagrama mostrando outra relação muitos para muitos entre autores, books_authors e livros.

No diagrama, você pode ver que há uma tabela chamada books_authors que está vinculando autores com seus livros e livros com seus autores. Essa tabela de vinculação não precisa ser exposta ao usuário final. A tabela de vinculação é apenas um artefato para permitir que a relação Muitos para Muitos exista, mas o Construtor de API de Dados precisa saber sua existência para usá-la corretamente.

A CLI do DAB pode ser usada para criar a relação Muitos para Muitos e também configurar o objeto de vinculação (remova todas as relações criadas na seção anterior e comece apenas com a Book entidade e Author sem nenhuma relação configurada entre elas:

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" 

O que atualiza o arquivo de configuração JSON para ser semelhante a este exemplo:

"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" ]
    }
  }
}

A configuração está informando ao DAB que você deseja adicionar um authors campo na entidade que permite o Book acesso aos autores do livro. authorspode ser many, portanto, uma lista de autores é retornada quando a consulta GraphQL acessa o authors campo. Essa relação define como navegar de livros para autores: os campos de banco de dados usados para navegar de livros para seus autores são definidos no source.fields para o livro e, no target.fields para os autores, de forma semelhante à relação Um para Muitos ou Muitos para Um descrita anteriormente neste artigo.

Essa relação é uma relação Muitos para Muitos, portanto, não há nenhuma conexão direta entre as duas entidades e, portanto, é necessário usar uma linking.object . No exemplo, a tabela dbo.books_authors de banco de dados é usada como o objeto de vinculação. Como o objeto de vinculação é capaz de conectar livros a seus autores é definido nas linking.source.fields propriedades e linking.target.fields . O primeiro informa ao DAB como a entidade de origem - a Book - está conectada ao objeto de gosto e a segunda como o objeto de vinculação está conectado à entidade de destino, Author no exemplo.

Para entender como as informações fornecidas são usadas, você pode usar este exemplo de consulta equivalente:

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 

Com a configuração fornecida, o DAB é capaz de entender GraphQL como este exemplo:

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

Onde você quer obter livros e seus autores.

Para permitir a navegação de Author para Book, os mesmos princípios podem ser aplicados, atualizando a configuração usando o seguinte comando:

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" 

Que define uma relação Muitos para Muitos entre a Author entidade e a Book entidade, usando o objeto dbo.books_authors de vinculação nos bastidores.