Схема GraphQL

SubQuery TeamОколо 4 мин

Схема GraphQL

Определение объектов

В файле schema.graphql определены различные схемы GraphQL. Из-за способа работы языка запросов GraphQL файл схемы по существу определяет форму ваших данных из SubQuery. Чтобы узнать больше о том, как писать на языке схем GraphQL, мы рекомендуем ознакомиться со схемами и типами.

Важно: когда вы вносите какие-либо изменения в файл схемы, убедитесь, что вы регенерируете каталог типов с помощью следующей команды yarn codegen

Объекты

Каждый объект должен определять требуемые поля id c помощью типа ID!. Это используется как главный ключ и он уникален среди всех объектов того же типа.

Не-нулевые поля в объектах обозначаются !. Посмотрите на пример ниже:

type Example @entity {
  id: ID! # поле id является обязательным и должно выглядеть так.
  name: String! # Это обязательное поле
  address: String # Это обязательное поле
}

Поддерживаемые скаляры и типы

В настоящее время мы поддерживаем следующие скалярные типы:

  • ID
  • Int
  • String
  • BigInt
  • Float
  • Date
  • Boolean
  • <EntityName> для вложенных отношений объектов вы можете использовать имя определенного объекта в качестве одного из полей. Смотрите в Entity Relationships.
  • JSON может также хранить структурированные данные, смотрите JSON type
  • <EnumName> типы это особый вид нумерованного скаляра, который ограничен определенным набором допустимых значений. Подробнее о Graphql Enumopen in new window

Индексирование по непервичному ключу

Для улучшения производительности запроса необходимо индексировать поле сущности выполнив @index для поля не являющегося первичным ключом.

Однако мы не позволяем юзерам добавлять аннотацию @index на какой либо из объектов JSON. По умолчанию индексы автоматически добавляются к внешним ключам и к полям JSON в базе данных, но только для повышения производительности службы запросов.

Вот пример.

type User @entity {
  id: ID!
  name: String! @index(unique: true) # unique может быть true или false
  title: Title! # Индексы автоматически добавляются к полю внешнего ключа 
}

type Title @entity {
  id: ID!  
  name: String! @index(unique:true)
}

Если предположить, что мы знаем имя этого пользователя, но не знаем точного значения Id, то вместо того, чтобы извлечь всех пользователей и затем фильтровать их по имени, мы можем добавить @index за полем имени. Это делает выполнение запросов значительно более быстрым, и мы можем дополнительно передать unique: true для обеспечения уникальности.

Если поле не является уникальным, то максимальный размер набора результатов будет равен 100

Запуск генерации кода автоматически создает getByName в модели User, а поле внешнего ключа title создает метод getByTitleId, к обоим методам можно получить прямой доступ в функции сопоставления.

/* Подготовить запись заголовка */
INSERT INTO заголовки (id, имя) VALUES ('id_1', 'Captain')
// Обработчик в mapping функции
import {User} from "../types/models/User"
import {Title} from "../types/models/Title"

const jack = await User.getByName('Jack Sparrow');

const captainTitle = await Title.getByName('Captain');

const pirateLords = await User.getByTitleId(captainTitle.id); // Список всех Captains

Отношения объектов

Объекты часто имеют вложенные взаимоотношения с другими объектами. Установка значения в поле имени другого объекта по умолчанию будет означать отношение один-к-одному между этими двумя сущностями.

Различные взаимоотношения объектов («один-к-одному», «один-ко-многим» и «многие-ко-многим») могут быть настроены с помощью примеров приведенных ниже.

Отношения Один-к-Одному

Отношения «один-к-одному» используются по умолчанию, когда только один объект сопоставлен с другим.

Пример: Паспорт будет принадлежать только одному человеку, и человек имеет только один паспорт (в этом примере):

type Person @entity {
  id: ID!
}

type Passport @entity {
  id: ID!
  owner: Person!
}

или

type Person @entity {
  id: ID!
  passport: Passport!
}

type Passport @entity {
  id: ID!
}

Отношения Один-ко-Многим

Вы можете использовать квадратные скобки, чтобы указать, что тип поля содержит несколько объектов.

Пример: Человек может иметь несколько учетных записей.

type Person @entity {
  id: ID!
  accounts: [Account]! @derivedFrom(field: "person") #This is virtual field 
}

type Account @entity {
  id: ID!
  person: Person!
}

Отношения Многие-ко-Многим

Отношения «многие ко многим» могут быть достигнуты путем реализации сопоставления объекта для соединения с двумя другими объектами.

Пример: Каждый человек является частью нескольких групп (ЧеловекГруппа), и в группах есть несколько разных людей (ЧеловекГруппа).

type Person @entity {
  id: ID!
  name: String!
}

type PersonGroup @entity {
  id: ID!
  person: Person!
  Group: Group!
}

type Group @entity {
  id: ID!
  name: String!
}

Кроме того, возможно создать связь одного и того же объекта в нескольких полях средних объектов.

Например, учетная запись может иметь несколько переводов, и каждая передача имеет исходную и конечную учетную запись.

Это установит двунаправленную связь между двумя Учетными Записями (от и до) через Таблицу Переводов.

type Account @entity {
  id: ID!
  публичный адрес: String!
}

type Transfer @entity {
  id: ID!
  amount: BigInt
  from: Account!
  to: Account!
}

Обратный просмотр

Чтобы включить обратный поиск объекта в отношении, прикрепите @dehibitedFrom к полю и укажите на его поле обратного просмотра другого объекта.

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

Передача «от» Учетной Записи доступна из объекта Учетной Записи путем установки значений отправитьПередачу (sentTransfer) или получитьПередачу (receiveTransfer), полученной из соответствующих полей из (from) или в (to).

type Account @entity {
  id: ID!
  публичный адрес: String!
  sentTransfers: [Transfer] @derivedFrom(field: "from")
  receivedTransfers: [Transfer] @derivedFrom(field: "to")
}

type Transfer @entity {
  id: ID!
  amount: BigInt
  from: Account!
  to: Account!
}

Тип JSON

Мы поддерживаем сохранение данных в формате JSON, который является быстрым способом хранения структурированных данных. Мы автоматически сгенерируем соответствующие JSON интерфейсы для запрашивания этих данных и сэкономим вам время на определение и управление объектами.

Мы рекомендуем пользователям использовать тип JSON в следующих сценариях:

  • Когда хранение структурированных данных в одном поле более удобно, чем создание нескольких отдельных сущностей.
  • При сохранении произвольных пользовательских настроек ключа/значения (где значение может быть логическим, текстовым или цифровым, и вы не хотите иметь отдельные столбцы для разного типа данных)
  • Схема непостоянна и часто меняется

Определение директивы JSON

Определите свойство как тип JSON, добавив аннотацию jsonField в сущность. Это автоматически создаст интерфейсы для всех объектов JSON в вашем проекте в types/interfaces.ts, и вы сможете получить к ним доступ в вашей функции отображения.

В отличие от сущности, объект директивы jsonField не требует поля id. Объект JSON также может быть вложен в другие объекты JSON.

type AddressDetail @jsonField {
  street: String!
  district: String!
}

type ContactCard @jsonField {
  phone: String!
  address: AddressDetail # Nested JSON
}

type User @entity {
  id: ID! 
  contact: [ContactCard] # Store a list of JSON objects
}

Запрос к полям JSON

Недостатком использования типов JSON является небольшое влияние на эффективность запросов при фильтрации, поскольку каждый раз при выполнении текстового поиска он выполняется по всей сущности.

Однако в нашей службе запросов это влияние все еще приемлемо. Вот пример использования оператора contains в GraphQL-запросе на поле JSON для поиска первых 5 пользователей, владеющих номером телефона, который содержит '0064'.

# Чтобы найти первые 5 телефонных номеров пользователей, содержащих '0064'.

query{
  user(
    first: 5,
    filter: {
      contactCard: {
        contains: [{ phone: "0064" }]
    }
}){
    nodes{
      id
      contactCard
    }
  }
}