Les requêtes GraphQL avec les tables de jointure

je suis en train d'apprendre GraphQL alors j'ai construit un petit projet. Disons que j'ai 2 modèles, User et 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,

});

les relations sont 1: many, où un utilisateur peut avoir beaucoup de commentaires.

J'ai construit le schéma comme ceci:

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()
    }
  })
});

Et la requête:

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

exécuter la requête pour un utilisateur et ses commentaires se font avec 1 requête, comme ceci:

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

si je comprends bien, le client obtiendra les résultats en utilisant 1 requête, c'est l'avantage d'utiliser GraphQL. Mais le serveur exécutera 2 requêtes, une pour l'utilisateur et une autre pour ses commentaires.

Ma question Est, quelles sont les meilleures pratiques pour construire le GraphQL schema et types et combinaison de jointure entre les tables, de sorte que le serveur pourrait également exécuter la requête avec 1 requête?

18
demandé sur itaied 2016-12-03 11:49:06

2 réponses

le concept auquel vous faites référence est appelé batching. Il existe plusieurs bibliothèques qui offrent ce. Par exemple:

  • Dataloader: utilitaire Générique maintenu par Facebook qui fournit "une API cohérente sur les différents backends et réduit les requêtes à ces backends via batching et caching"

  • rejoignez-monster: "Un GraphQL-à-requête SQL couche d'exécution pour le lot de données récupérer."

10
répondu marktani 2016-12-14 18:48:04

Pour quiconque de l'utiliser .NET et le GraphQL pour .NET paquet, j'ai fait une méthode d'extension qui convertit la requête GraphQL en Entity Framework Includes.

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;
            }
        }
    }
}

disons que nous avons la requête suivante:

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

nous pouvons créer une variable dans la méthode" resolve " du champ:

var include = context.GetIncludeString();

qui génère la chaîne suivante:

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

et le transmettre à l'Entité Framwork:

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));
}
0
répondu ceferrari 2017-10-29 12:24:44