sequelize postgres node sql node.js sequelize.js graphql

sql - postgres - sequelize mongodb



Consultas GraphQL con tablas unidas (2)

Estoy aprendiendo GraphQL así que construí un pequeño proyecto. Digamos que tengo 2 modelos, User y Comment .

const Comment = Model.define(''Comment'', { content: { type: DataType.TEXT, allowNull: false, validate: { notEmpty: true, }, }, }); const User = Model.define(''User'', { name: { type: DataType.STRING, allowNull: false, validate: { notEmpty: true, }, }, phone: DataType.STRING, picture: DataType.STRING, });

Las relaciones son 1: muchas, donde un usuario puede tener muchos comentarios.
He construido el esquema de esta manera:

const UserType = new GraphQLObjectType({ name: ''User'', fields: () => ({ id: { type: GraphQLString }, name: { type: GraphQLString }, phone: { type: GraphQLString }, comments: { type: new GraphQLList(CommentType), resolve: user => user.getComments() } }) });

Y la consulta:

const user = { type: UserType, args: { id: { type: new GraphQLNonNull(GraphQLString) } }, resolve(_, {id}) => User.findById(id) };

La ejecución de la consulta para un usuario y sus comentarios se realiza con 1 solicitud, así:

{ User(id:"1"){ Comments{ content } } }

Según tengo entendido, el cliente obtendrá los resultados utilizando 1 consulta, este es el beneficio de utilizar GraphQL . Pero el servidor ejecutará 2 consultas, una para el usuario y otra para sus comentarios.
Mi pregunta es, ¿cuáles son las mejores prácticas para crear el esquema y los tipos de GraphQL y combinar la combinación entre tablas, de modo que el servidor también pueda ejecutar la consulta con 1 solicitud?


El concepto al que se refiere se llama procesamiento por lotes. Hay varias bibliotecas por ahí que ofrecen esto. Por ejemplo:

  • Dataloader : utilidad genérica mantenida por Facebook que proporciona "una API consistente en varios backends y reduce las solicitudes a esos backends a través de lotes y almacenamiento en caché"

  • join-monster : "Una capa de ejecución de consultas GraphQL a SQL para la obtención de datos por lotes".


Para cualquier persona que use .NET y el paquete GraphQL for .NET , he creado un método de extensión que convierte la consulta de GraphQL en Entity Framework Incluye.

public static class ResolveFieldContextExtensions { public static string GetIncludeString(this ResolveFieldContext<object> source) { return string.Join('','', GetIncludePaths(source.FieldAst)); } private static IEnumerable<Field> GetChildren(IHaveSelectionSet root) { return root.SelectionSet.Selections.Cast<Field>() .Where(x => x.SelectionSet.Selections.Any()); } private static IEnumerable<string> GetIncludePaths(IHaveSelectionSet root) { var q = new Queue<Tuple<string, Field>>(); foreach (var child in GetChildren(root)) q.Enqueue(new Tuple<string, Field>(child.Name.ToPascalCase(), child)); while (q.Any()) { var node = q.Dequeue(); var children = GetChildren(node.Item2).ToList(); if (children.Any()) { foreach (var child in children) q.Enqueue(new Tuple<string, Field> (node.Item1 + "." + child.Name.ToPascalCase(), child)); } else { yield return node.Item1; } }}}

Digamos que tenemos la siguiente consulta:

query { getHistory { id product { id category { id subCategory { id } subAnything { id } } } } }

Podemos crear una variable en el método "resolver" del campo:

var include = context.GetIncludeString();

que genera la siguiente cadena:

"Product.Category.SubCategory,Product.Category.SubAnything"

y pasarlo a Entity Framework:

public Task<TEntity> Get(TKey id, string include) { var query = Context.Set<TEntity>(); if (!string.IsNullOrEmpty(include)) { query = include.Split('','', StringSplitOptions.RemoveEmptyEntries) .Aggregate(query, (q, p) => q.Include(p)); } return query.SingleOrDefaultAsync(c => c.Id.Equals(id)); }