Как не выставлять дубликаты (нормализовать?) узлы через GraphQL?

#optimization #graphql #normalization

Вопрос:

Учитывая, что «у пользователя много ссылок» (что означает, что ссылка была создана пользователем), я хочу разработать API для извлечения ссылок вместе с пользователями, чтобы возвращаемые данные не содержали дублированных пользователей.

Другими словами, вместо этого запроса:

 query {
  links {
    id
    user {
      id email
    }
  }
}
 

это возвращает следующие данные:

 {
  "data": {
    "links": [
      {
        "id": 1,
        "user": {
          "id": 2,
          "email": "user2@example.com"
        }
      },
      {
        "id": 2,
        "user": {
          "id": 2,
          "email": "user2@example.com"
        }
      }
  ]
  }
}
 

Я хочу сделать такой запрос (обратите внимание на колонку «ссылки»):

 query {
  links {
    id
    userId
  }
  references {
    users {
      id
      email
    }
  }
}
 

это возвращает связанных пользователей без дубликатов:

 {
  "data": {
    "links": [
      {
        "id": 1,
        "userId": 2
      },
      {
        "id": 2,
        "userId": 2
      },
    ],
    "references": {
      "users": [
        {
          "id": 2,
          "email": "user2@example.com"
        }
      ]
    }
  }
}
 

Это должно уменьшить объем данных, передаваемых между клиентом и сервером, что немного повышает скорость.

Существует ли готовая общая реализация этой идеи на любом языке? (В идеале, в поисках Руби)

Ответ №1:

Это не запрос или роль сервера для нормализации данных.

  • в спецификациях GraphQL таких возможностей нет;
  • сервер должен возвращать все запрошенные поля в структуре запрошенного [ответа] ;

… но вы можете реализовать некоторые:

  • стандартная (обычно используемая) разбивка на страницы (стиль ретрансляции edges / nodes , nodes только или лучше и то, и другое);
  • веса запросов [сложности] для продвижения этого оптимизированного стиля запросов — отдельная проблема;
  • поле справочного словаря в пределах запрашиваемого типа;
       links {
        egdes {
          node {
            id
            title
            url
            authorId
    #  possible but limited usage with heavy weights
    #       author {
    #         id
    #         email
    #       }
          }
        }
        pageInfo {
          hasNextPages
        }
        referencedUsers {
          id
          email
        }
      }
 

где:

  • User имеет id и email реквизит;
  • referencedUsers это [User!] тип;
  • node.author это User тип;

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

Вы можете визуализировать (реагировать?) некоторый <User/> компонент (внутри <Link /> компонента), передаваемый node.authorId в качестве аргумента, например <User id={authorId} /> . Пользовательский компонент может useQuery подключаться к cache-only политике для чтения реквизитов/полей пользователя.

Подробности см. в документах Apollo. Вы должны реализовать это для себя и документировать это, чтобы помочь/направлять пользователей API.

Комментарии:

1. Спасибо! Да, я знаю, что в Apollo есть система кэширования, которая не будет запрашивать данные из бэкэнда, если они уже получены с помощью ключа Type#id . Это, конечно, приятно. Но вопрос больше связан с уменьшением трафика данных между сервером и клиентом.

2. И как реализовать этот хитрый распознаватель, который позволил бы выбирать объекты, на которые ссылаются, в отдельной структуре.

3. [вероятно] вы не уменьшите трафик без взвешенных запросов … ИДК руби … в общем случае [поток разрешения graphql] это может быть похоже … links распознаватель считывает записи из одной таблицы БД, просто собирает идентификаторы пользователей в некоторый массив (сохраняет links объект результата рядом с ребрами/узлами и информацией о разбивке на страницы), использует его в referencedUsers распознавателе (по parent 1-му аргументу) для извлечения пользователей из 2-й таблицы БД