Ошибка при попытке рекурсивно выполнить запрос GraphQL вместе с запрашиваемыми результатами - PullRequest
0 голосов
/ 20 сентября 2018

Это тесно связано с моим последним вопросом здесь .Короче говоря, у меня есть 2 схемы: dbPosts и dbAuthors .Они выглядят примерно так (для краткости я опустил некоторые поля здесь):

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

Я разрешаю свой пост запросов вот так:

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, если вы хотите увидеть результаты для себя.

Большое спасибо за ваше время, если вы зашли так далеко.Буду очень признателен за любой указатель в правильном направлении! "

PS : Если вы заметили, я записываю значения 3 переменных в каждом преобразователе для целей отладки:

  1. queriedFields : массив всех запрашиваемых полей
  2. fieldsInParent : массив всех полей, возвращаемых в parent резолверасвойство
  3. доступно : логическое значение, показывающее, существуют ли все 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 resolver не может выполняться только один раз?Забавно, что в первых двух журналах fieldsInParent отсутствует поле posts , даже если схема для author включает такое поле.

1 Ответ

0 голосов
/ 20 сентября 2018

Ваш результат запроса не фактически включает все запрошенные данные.Запрос posts преобразуется в массив, который включает один объект Post и ноль.Ноль есть, потому что GraphQL попытался полностью разрешить другой объект Post и не смог - он столкнулся с ошибкой проверки, а именно, что автор сообщения разрешил в ноль.

Вы можете изменить схему, чтобы сделатьauthor поле nullable, которое избавит от ошибки, но все равно оставит вас с нулевым сообщением.Предположительно, если сообщение существует, у него должен быть автор (хотя с MongoDB я думаю, очень возможно, что у вас просто плохие данные).Если вы загляните внутрь своего распознавателя, есть два оператора return - одно из них (вероятно, вызов db) возвращает ноль для этого второго сообщения.

В качестве клиента, вы, вероятно, неЯ не хочу иметь дело с нулями внутри массива и хочу пустой массив вместо нуля для всего поля.При использовании списков (массивов) вы можете захотеть сделать их не обнуляемыми и сделать каждый элемент в этом списке также обнуляемым.Вы делаете это следующим образом:

posts: [Post!]!

Вам все еще нужно убедиться, что ваша логика решателя предотвращает возникновение этих нулей, но добавление проверки может помочь вам легче отследить подобное поведение.

...