Связь между GraphQL и базой данных при использовании соединения и разбивки на страницы?

С помощью Relay очень легко настроить разбиение на страницы, однако есть небольшая деталь, которая мне непонятна.

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

const postType = new GraphQLObjectType({
  name: 'Post',
  fields: () => ({
      id: globalIdField('Post'),
      title: {
        type: GraphQLString
      },
  }),
  interfaces: [nodeInterface],
})

const userType = new GraphQLObjectType({
  name: 'User',
  fields: () => ({
      id: globalIdField('User'),
      email: {
        type: GraphQLString
      },
      posts: {
        type: postConnection,
        args: connectionArgs,
        resolve: async (user, args) => {
          // getUserPosts() is in next code block -> it gets the data from db
          // I pass args (e.g "first", "after" etc) and user id (to get only user posts)
          const posts = await getUserPosts(args, user._id)
          return connectionFromArray(posts, args)
        }
      },
  }),
  interfaces: [nodeInterface],
})

const {connectionType: postConnection} = 
              connectionDefinitions({name: 'Post', nodeType: postType})

exports.getUserPosts = async (args, userId) => {
    try {
      // using MongoDB and Mongoose but question is relevant with every db
      // .limit() -> how many posts to return
      const posts = await Post.find({author: userId}).limit(args.first).exec()
      return posts
    } catch (err) {
      return err
    }
}

Причина моего замешательства:

  • Если я передам аргумент first и использую его в запросе БД для ограничения возвращаемых результатов, hasNextPage будет всегда false. Это эффективно, но нарушает hasNextPage (hasPreviousPage, если вы используете last)
  • If I don't pass the first argument and don't use it in db query to limit returned results, hasNextPage is working as expected but it will return all the items I queried (could be thousands)
    • Even if database is on same machine (which isn't the case for bigger apps), this seems very, very, very inefficient and awful. Please prove me that Im wrong!
    • Насколько я знаю, GraphQL не имеет кэширования на стороне сервера, поэтому нет смысла возвращать все результаты (даже если бы это было так, пользователи не просматривают 100% контента)

В чем здесь логика?

Одно из решений, которое приходит мне на ум, состоит в том, чтобы добавить +1 к значению first в getUserPosts, это приведет к извлечению одного лишнего элемента, и hasNextPage, вероятно, сработает. Но это похоже на взлом, и всегда возвращается лишний элемент — он будет расти относительно быстро, если будет много connections и запросов.

Ожидается ли, что мы взломаем его таким образом? Ожидается ли возврат всех результатов?

Или я неправильно понял всю связь между базой данных и GrahpQL/Relay?


Что, если бы я использовал FB DataLoader и Redis? Изменит ли это что-нибудь в этой логике?


person Solo    schedule 02.06.2016    source источник


Ответы (1)


Причина моего замешательства

Вспомогательная функция connectionFromArray библиотеки graphql-relay-js НЕ является решением для всех видов нумерации страниц. Нам нужно адаптировать наш подход на основе наших предпочтительных моделей разбиения на страницы.

Функция connectionFromArray получает значения hasNextPage и hasPrevisousPage из заданного массива. Итак, то, что вы заметили и упомянули в причине моего замешательства, является ожидаемым поведением.

Что касается вашей путаницы, загружать все данные или нет, это зависит от решаемой проблемы. Загрузка всех элементов может иметь смысл в нескольких ситуациях, таких как:

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

Две распространенные модели пагинации — нумерованные страницы и бесконечная прокрутка. Спецификация соединения GraphQL не имеет отношения к модели разбивки на страницы и допускает обе из них.

Для пронумерованных страниц вы можете использовать дополнительное поле totalPost в своем типе GraphQL, которое можно использовать для отображения ссылок на пронумерованные страницы в вашем пользовательском интерфейсе. На бэкенде вы можете использовать такую ​​функцию, как skip, чтобы получать только необходимые элементы. Поле totalPost и текущий номер страницы устраняют зависимость от hasNextPage или hasPreviousPage.

Для бесконечной прокрутки вы можете использовать поле cursor, которое можно использовать в качестве значения для after в вашем запросе. В серверной части вы можете использовать значение cursor для получения следующих элементов (значение first). См. пример использования курсора в документации Relay по соединению GraphQL. См. этот ответ о соединении и курсоре GraphQL. См. это и это сообщения в блоге, которые помогут вам лучше понять идею cursor.


Какая здесь логика?

Ожидается ли, что мы взломаем его таким образом?

Нет, в идеале мы не должны взломать и забыть об этом. Это оставит технический долг в проекте, что, вероятно, вызовет больше проблем в долгосрочной перспективе. Вы можете рассмотреть возможность реализации собственной функции для возврата объекта подключения. Вы получите представление о том, как это сделать, при реализации соединение с массивом в graphql-relay-js.

Ожидается ли возврат всех результатов?

Опять же, зависит от проблемы.


Что, если бы я использовал FB DataLoader и Redis? Изменит ли это что-нибудь в этой логике?

Вы можете использовать библиотеку загрузчика данных facebook для кэширования и пакетной обработки ваших запросов. Redis — еще один вариант кэширования результатов. Если вы загружаете (1) все элементы с помощью загрузчика данных или сохраняете все элементы в Redis и (2) элементы легковесны, вы можете легко создать массив всех элементов (следуя принципу KISS). Если элементы имеют большой вес, создание массива может оказаться дорогостоящей операцией.

person Ahmad Ferdous    schedule 03.06.2016