【问题标题】:Error while trying to run a GraphQL query recursively, along with queried results尝试递归运行 GraphQL 查询以及查询结果时出错
【发布时间】:2019-02-24 09:10:31
【问题描述】:

这与我上一个问题here 密切相关。简而言之,我有 2 个架构,dbPostsdbAuthors。它们看起来有点像这样(为简洁起见,我在这里省略了一些字段):

dbPosts

id: mongoose.Schema.Types.ObjectId,
title: { type: String },
content: { type: String },
excerpt: { type: String },
slug: { type: String },
author: {
   id: { type: String },
   fname: { type: String },
   lname: { type: String },
}

dbAuthors

id: mongoose.Schema.Types.ObjectId,
fname: { type: String },
lname: { type: String },
posts: [
         id: { type: String },
         title: { type: String }
       ]

我正在解决我的 post 查询,如下所示:

const mongoose = require('mongoose');
const graphqlFields = require('graphql-fields');
const fawn = require('fawn');
const dbPost = require('../../../models/dbPost');
const dbUser = require('../../../models/dbUser');

fawn.init(mongoose);

module.exports = {
  // Queries
  Query: {
    posts: (root, args, context) => {
      return dbPost.find({});
    },
    post: (root, args, context) => {
      return dbPost.findById(args.id);
    },
  },

  Post: {
    author: (parent, args, context, ast) => {
      // Retrieve fields being queried
      const queriedFields = Object.keys(graphqlFields(ast));
      console.log('-------------------------------------------------------------');
      console.log('from Post:author resolver');
      console.log('queriedFields', queriedFields);
      // Retrieve fields returned by parent, if any
      const fieldsInParent = Object.keys(parent.author);
      console.log('fieldsInParent', fieldsInParent);
      // Check if queried fields already exist in parent
      const available = queriedFields.every((field) => fieldsInParent.includes(field));
      console.log('available', available);
      if(parent.author && available) {
        return parent.author;
      } else {
        return dbUser.findOne({'posts.id': parent.id});
      }
    },
  },
};

我正在解决所有作者这样的查询:

const mongoose = require('mongoose');
const graphqlFields = require('graphql-fields');
const dbUser = require('../../../models/dbUser');
const dbPost = require('../../../models/dbPost');

module.exports = {
  // Queries
  Query: {
    authors: (parent, root, args, context) => {
      return dbUser.find({});
    },
    author: (root, args, context) => {
      return dbUser.findById(args.id);
    },
  },

  Author: {
    posts: (parent, args, context, ast) => {
      // Retrieve fields being queried
      const queriedFields = Object.keys(graphqlFields(ast));
      console.log('-------------------------------------------------------------');
      console.log('from Author:posts resolver');
      console.log('queriedFields', queriedFields);
      // Retrieve fields returned by parent, if any
      const fieldsInParent = Object.keys(parent.posts[0]._doc);
      console.log('fieldsInParent', fieldsInParent);
      // Check if queried fields already exist in parent
      const available = queriedFields.every((field) => fieldsInParent.includes(field));
      console.log('available', available);
      if(parent.posts && available) {
        // If parent data is available and includes queried fields, no need to query db
        return parent.posts;
      } else {
        // Otherwise, query db and retrieve data
        return dbPost.find({'author.id': parent.id, 'published': true});
      }
    },
  },
};

再次,为了简洁起见,我省略了与这个问题无关的部分,例如突变。我的目标是使所有查询以递归方式工作,同时优化数据库查找。但不知何故,我无法做到这一点。例如,这是我正在运行的一个查询:

{
  posts{
    id
    title
    author{
      first_name
      last_name
      id
      posts{
        id
        title
      }
    }
  }
}

它返回这个:

{
  "errors": [
    {
      "message": "Cannot return null for non-nullable field Post.author.",
      "locations": [
        {
          "line": 5,
          "column": 5
        }
      ],
      "path": [
        "posts",
        1,
        "author"
      ]
    }
  ],
  "data": {
    "posts": [
      {
        "id": "5ba1f3e7cc546723422e62a4",
        "title": "A Title!",
        "author": {
          "first_name": "Bill",
          "last_name": "Erby",
          "id": "5ba130271c9d440000ac8fc4",
          "posts": [
            {
              "id": "5ba1f3e7cc546723422e62a4",
              "title": "A Title!"
            }
          ]
        }
      },
      null
    ]
  }
}

如果您注意到,此查询确实会返回所有请求的值,但还会针对 post.author 查询添加错误消息!这可能是什么原因造成的?

我没有包含整个代码库以免让事情变得混乱,但如果你想看一下,它在 Github 上,GraphiQL 界面在 https://graph.schandillia.com 上,如果你想看的话自己的结果。

非常感谢您抽出宝贵时间,如果您已经走到了这一步。非常感谢任何指向正确方向的指针!”

P.S.:如果您注意到,我会在每个解析器中记录 3 个变量的值以进行调试:

  1. queriedFields:所有被查询字段的数组
  2. fieldsInParent:在解析器的 parent 属性中返回的所有字段的数组
  3. available:显示所有 queriedFields 成员是否存在于 fieldsInParent 中的布尔值

当我运行这样的简单查询时:

{
  posts{
    id
    author{
      id
      posts{
        id
      }
    }
  }
}

这是记录的内容:

-------------------------------------------------------------
from Post:author resolver
queriedFields [ 'id', 'posts' ]
fieldsInParent [ '$init', 'id', 'first_name', 'last_name' ]
available false
-------------------------------------------------------------
from Post:author resolver
queriedFields [ 'id', 'posts' ]
fieldsInParent [ '$init', 'id', 'first_name', 'last_name' ]
available false
-------------------------------------------------------------
from Author:posts resolver
queriedFields [ 'id' ]
fieldsInParent [ 'id', 'title' ]
available true

post:author 解析器不应该只执行一次吗?此外,有趣的是,在前 2 个日志中,fieldsInParent 缺少 posts 字段,即使 author 的架构包含这样的字段。

【问题讨论】:

    标签: graphql resolver


    【解决方案1】:

    您的查询结果实际上包含所有请求的数据。 posts 查询解析为包含一个 Post 对象和一个空值的数组。 null 存在是因为 GraphQL 尝试完全解析另一个 Post 对象但无法 - 它遇到了验证错误,即帖子的作者解析为 null。

    您可以更改架构以使 author 字段可为空,这样可以消除错误,但仍会留下空帖子。据推测,如果一个帖子存在,它应该有一个作者(虽然我猜使用 MongoDB,你很可能只是有一些坏数据)。如果您查看解析器内部,有两个 return 语句 - 其中一个(可能是 db 调用)为第二个帖子返回 null。

    顺便说一句,作为客户,您可能不想处理数组内部的空值,而是希望整个字段都使用空数组而不是空值。使用列表(数组)时,您可能希望使它们都不可为空,并使该列表中的每个项目也不可为空。你这样做:

    posts: [Post!]!
    

    您仍然需要确保您的解析器逻辑防止这些空值发生,但添加验证可以帮助您更轻松地捕捉到这种行为。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-29
      • 2020-01-31
      • 1970-01-01
      • 1970-01-01
      • 2020-06-01
      • 2019-12-29
      相关资源
      最近更新 更多