Si te encuentras con algo que te causa duda puedes comentarlo y te responderemos tan rápido como podamos.
Solución:
GraphQL como lenguaje de consulta en el front-end no admite “uniones” en el sentido clásico de SQL.
Más bien, le permite elegir qué campos de un modelo en particular desea obtener para su componente.
Para consultar todos los teléfonos en su conjunto de datos, su consulta se vería así:
query myComponentQuery
phone
id
brand
model
price
El servidor GraphQL que está consultando su front-end tendría resolutores de campo individuales, diciéndole a GraphQL dónde obtener la identificación, la marca, el modelo, etc.
La resolución del lado del servidor se vería así:
Phone:
id(root, args, context)
pg.query('Select * from Phones where name = ?', ['blah']).then(d => /*doStuff*/)
//OR
fetch(context.upstream_url + '/thing/' + args.id).then(d => /*doStuff*/)
return /*the result of either of those calls here*/
,
price(root, args, context)
return 9001
,
,
TL;DR
Sí, GraphQL admite una especie de pseudo-unión. Puede ver el ejemplo de libros y autores a continuación ejecutándose en mi proyecto de demostración.
Ejemplo
Considere un diseño de base de datos simple para almacenar información sobre libros:
create table Book ( id string, name string, pageCount string, authorId string );
create table Author ( id string, firstName string, lastName string );
Como sabemos que el autor puede escribir muchos libros, ese modelo de base de datos los coloca en tablas separadas. Aquí está el esquema de GraphQL:
type Query
bookById(id: ID): Book
type Book
id: ID
name: String
pageCount: Int
author: Author
type Author
id: ID
firstName: String
lastName: String
Fíjate que no hay authorId
sobre el Book
tipo pero un tipo Author
. La base de datos authorId
columna en la mesa de libros no está expuesta al mundo exterior. Es un detalle interno.
Podemos recuperar un libro y su autor usando esta consulta de GraphQL:
bookById(id:"book-1")
id
name
pageCount
author
firstName
lastName
Aquí hay una captura de pantalla en acción usando mi proyecto de demostración:
El resultado anida los detalles del autor:
"data":
"book1":
"id": "book-1",
"name": "Harry Potter and the Philosopher's Stone",
"pageCount": 223,
"author":
"firstName": "Joanne",
"lastName": "Rowling"
La única consulta de GQL dio como resultado dos llamadas separadas de búsqueda por id a la base de datos. Cuando una sola consulta lógica se convierte en múltiples consultas físicas, podemos encontrarnos rápidamente con la infame N+1
problema.
los N+1
Problema
En nuestro caso anterior, un libro solo puede tener un autor, por lo que solo obtenemos una “amplificación de lectura” en nuestra base de datos de 2x. Imaging si agrega un campo en Author
llamado books
de tipo [Book]
. Puede probar con algunos datos en los que un autor solo tiene uno o dos libros para buscar y agregar al array. Luego, en el sistema de producción real encuentras que un autor ha publicado 1.075 libros. Emitir 1.076 consultas (que es el +1 para el autor) probablemente provocará un tiempo de espera para sus clientes.
La solución fácil para ese ejemplo es no exponer un campo books
sobre el Autor. Mi demostración evita eso. Si quisiéramos que alguien pudiera consultar todos los libros de un autor, podríamos proporcionar una consulta adicional:
booksByAuthor(authorId: ID) [Book]
Obviamente, podría crear consultas adicionales al lastName
o lo que sea y agregue índices de base de datos para admitir esa consulta. Esta solución muestra que “no existe una bala mágica” sobre cómo asignar el “modelo lógico” al “modelo físico” cuando se trata de rendimiento. Esto se conoce como el problema de desajuste de impedancia relacional de objeto. Más sobre eso a continuación.
¿Es tan malo recuperar por ID?
Tenga en cuenta que el comportamiento predeterminado de GraphQl sigue siendo muy útil. Puede mapear GraphQL en cualquier cosa. Puede asignarlo a las API REST internas. Puede asignar algunos tipos a una base de datos relacional y otros tipos a una base de datos NoSQL. Estos pueden estar en el mismo esquema y el mismo punto final de GraphQL. No hay ninguna razón por la que no puedas tener Author
almacenado en Postgres y Book
almacenado en MongoDB. Esto se debe a que GraphQL no “se une al almacén de datos” de forma predeterminada, obtendrá cada tipo de forma independiente y generará la respuesta en la memoria para enviarla al cliente. Eso mayo sea el caso de que puede usar un modelo que solo se une a un pequeño conjunto de datos que obtiene muy buenos resultados de caché. Luego puede agregar almacenamiento en caché a su sistema y no tener ningún problema y beneficiarse de todas las ventajas de GraphQL.
¿Qué pasa con ORM?
Hay un proyecto llamado Join Monster que analiza el esquema de su base de datos, analiza la consulta GraphQL en tiempo de ejecución e intenta generar uniones de base de datos eficientes sobre la marcha. Esa es una forma de mapeo relacional de objetos que a veces recibe mucho “Odio Orm”. Esto se debe principalmente al problema de desajuste de impedancia relacional entre objetos.
En mi experiencia, cualquier ORM funciona si escribe el modelo de la base de datos para admitir exactamente la API de su objeto. En mi experiencia, cualquier ORM tiende a fallar cuando tiene un modelo de base de datos existente que intenta mapear con un marco ORM.
En mi humilde opinión, si el modelo de datos está optimizado sin pensar en ORM o consultas, entonces evite ORM. Por ejemplo, si el modelo de datos está optimizado para conservar espacio en la tercera forma normal clásica. Mi recomendación es evitar consultar el modelo de datos principal y usar el patrón CQRS. Vea a continuación un ejemplo.
¿Qué es práctico?
Si no desea optar por un ORM completo como Join Monster, puede optar por comenzar evitando exponer costosos N+1
escenarios en su API. Según el ejemplo anterior, puede evitar tener una lista de books: [Book]
en Author
y agregue una consulta GQL específica que vaya directamente a una consulta escrita a mano.
Si desea utilizar pseudo-uniones en GraphQL pero se encuentra con un N+1
problema, puede escribir código para mapear “obtenciones de campo” específicas en consultas de bases de datos escritas a mano. Esté atento a su esquema cada vez que algún campo devuelva un array.
Incluso cuando puede realizar consultas escritas a mano, puede encontrar escenarios en los que esas uniones no se ejecutan lo suficientemente rápido. En cuyo caso, considere el patrón CQRS y desnormalice parte del modelo de datos para permitir búsquedas rápidas. Por ejemplo, ANSI SQL le permite empaquetar matrices de datos o mapas de datos en una columna. Esto significa que puede tener una columna en la tabla de autores que incluya hasta 1075 títulos de libros e ID de libros que escribieron. Si a sus DBA no les gusta tenerlo en la tabla principal, colóquelo en una tabla separada y cree una vista de base de datos que una la tabla de autor tradicional con la vista optimizada de consulta. Ese sería un ejemplo del uso del patrón CQRS para acelerar las lecturas. Entonces, su problema se convierte en asegurarse de que cuando un autor publique un nuevo libro, usted escriba una versión actualizada. array de libros en el optimizado para consultas array columna.
Si haces scroll puedes encontrar las reseñas de otros usuarios, tú aún eres capaz insertar el tuyo si te apetece.