尝试递归运行GraphQL查询以及查询结果时出错

尝试递归运行GraphQL查询以及查询结果时出错,graphql,resolver,Graphql,Resolver,这与我的最后一个问题密切相关。简而言之,我有两个模式,dbPosts和dbAuthors。它们看起来有点像这样为了简洁起见,我省略了一些字段: DBPOST 作者 我正在这样解决我的帖子查询: const mongoose = require('mongoose'); const graphqlFields = require('graphql-fields'); const fawn = require('fawn'); const dbPost = require('../../../mod

这与我的最后一个问题密切相关。简而言之,我有两个模式,dbPosts和dbAuthors。它们看起来有点像这样为了简洁起见,我省略了一些字段:

DBPOST

作者

我正在这样解决我的帖子查询:

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
    author{
      id
      posts{
        id
      }
    }
  }
}
posts: [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
    author{
      id
      posts{
        id
      }
    }
  }
}
posts: [Post!]!
为了简洁起见,我再次省略了与这个问题无关的部分,例如突变。我的目标是使所有查询递归工作,同时优化数据库查找。但不知何故,我无法做到这一点。下面是我正在运行的一个查询,例如:

{
  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查询添加一条错误消息!这可能是什么原因造成的

为了不让事情变得混乱,我没有包括整个代码库,但是如果您想看一看,它已经打开了,如果您希望自己看到结果,GraphiQL接口已经打开了

如果你能走到这一步,非常感谢你抽出时间。非常感谢任何指向正确方向的指针

注意:如果您注意到了,我正在每个解析器中记录3个变量的值,以便于调试:

queriedFields:查询的所有字段的数组 fieldsInParent:解析程序父属性中返回的所有字段的数组 可用:一个布尔值,显示fieldsInParent中是否存在所有queriedFields成员 当我运行这样一个简单的查询时:

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
    author{
      id
      posts{
        id
      }
    }
  }
}
posts: [Post!]!
以下是记录的内容:

-------------------------------------------------------------
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解析器不应该只执行一次吗?另外,有趣的是,在前两个日志中,fieldsInParent缺少posts字段,即使author的模式包含这样一个字段。

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

您可以更改模式,使author字段可以为null,这将消除错误,但仍然会留下null post。假设,如果一篇文章存在,它应该有一个作者,尽管我猜在MongoDB中,你很可能只是有一些不好的数据。如果查看解析器内部,有两个返回语句-其中一个可能是db调用为第二个post返回null

另一方面,作为客户机,您可能不想在数组中处理null,而是希望整个字段都是空数组而不是null。使用列表数组时,您可能希望使它们都不可为null,并且使该列表中的每个项也不可为null。你是这样做的:

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
    author{
      id
      posts{
        id
      }
    }
  }
}
posts: [Post!]!
您仍然需要确保解析器逻辑可以防止这些空值的发生,但是添加验证可以帮助您更容易地捕获此类行为