Docs
Поиск⌘ K
  • Главная страница
  • О The Graph
  • Поддерживаемые сети
  • Protocol Contracts
  • Субграфы
    • Субпотоки
      • Token API
        • AI Suite
          • Индексирование
            • Ресурсы
              Субграфы > Разработка > Создание

              8 минуты

              Схема GraphQL

              Обзор

              Схема для вашего субграфа находится в файле schema.graphql. Схемы GraphQL определяются с использованием языка определения интерфейса GraphQL.

              Примечание: Если вы никогда не писали схему GraphQL, рекомендуется ознакомиться с этим введением в систему типов GraphQL. Справочную документацию по схемам GraphQL можно найти в разделе GraphQL API.

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

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

              • Все запросы будут выполняться против модели данных, определенной в схеме субграфа. Поэтому проектирование схемы субграфа должно основываться на запросах, которые ваше приложение будет выполнять.
              • Может быть полезно представить объекты как «объекты, содержащие данные», а не как события или функции.
              • Вы определяете типы объектов в файле schema.graphql, и Graph Node будет генерировать поля верхнего уровня для запроса отдельных экземпляров и коллекций этих типов объектов.
              • Каждый тип, который должен быть объектом, должен быть аннотирован директивой @entity.
              • По умолчанию объекты изменяемы, то есть мэппинги могут загружать существующие объекты, изменять и сохранять их новую версию.
                • Изменяемость имеет свою цену, поэтому для типов объектов, которые никогда не будут изменяться, например, содержащих данные, извлеченные дословно из чейна, рекомендуется пометить их как неизменяемые с помощью @entity(immutable: true).
                • Если изменения происходят в том же блоке, в котором был создан объект, то мэппинги могут вносить изменения в неизменяемые объекты. Неизменяемые объекты гораздо быстрее записываются и запрашиваются, поэтому их следует использовать, когда это возможно.

              Удачный пример

              Следующий объект Gravatar структурирован вокруг объекта Gravatar и является хорошим примером того, как можно определить объект.

              1type Gravatar @entity(immutable: true) {2  id: Bytes!3  owner: Bytes4  displayName: String5  imageUrl: String6  accepted: Boolean7}

              Неудачный пример

              Следующий пример объектов GravatarAccepted и GravatarDeclined основан на событиях. Не рекомендуется сопоставлять события или вызовы функций 1:1 к объектам.

              1type GravatarAccepted @entity {2  id: Bytes!3  owner: Bytes4  displayName: String5  imageUrl: String6}78type GravatarDeclined @entity {9  id: Bytes!10  owner: Bytes11  displayName: String12  imageUrl: String13}

              Дополнительные и обязательные поля

              Поля объектов могут быть определены как обязательные или необязательные. Обязательные поля указываются с помощью ! в схеме. Если поле является скалярным, вы получите ошибку при попытке сохранить объект. Если поле ссылается на другой объект, то вы получите следующую ошибку:

              1Null value resolved for non-null field 'name'

              Каждый объект должен иметь поле id, которое должно быть типа Bytes! или String!. Обычно рекомендуется использовать Bytes!, если только id не содержит текст, читаемый человеком, поскольку объекты с id типа Bytes! будут быстрее записываться и запрашиваться, чем те, у которых id типа String!. Поле id служит основным ключом и должно быть уникальным среди всех объектов одного типа. По историческим причинам также принимается тип ID!, который является синонимом String!.

              Для некоторых типов объектов id для Bytes! формируется из id двух других объектов. Это возможно с использованием функции concat, например, let id = left.id.concat(right.id), чтобы сформировать id из id объектов left и right. Аналогично, чтобы сформировать id из id существующего объекта и счетчика count, можно использовать let id = left.id.concatI32(count). Конкатенация гарантирует, что id будет уникальным, если длина left.id одинаковая для всех таких объектов, например, если left.id — это Address.

              Встроенные скалярные типы

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

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

              ТипОписание
              BytesМассив байтов, представленный в виде шестнадцатеричной строки. Обычно используется для хэшей и адресов Ethereum.
              StringСкаляр для значений типа string. Нулевые символы не поддерживаются и автоматически удаляются.
              BooleanСкаляр для значений boolean.
              IntСпецификация GraphQL определяет тип Int как знаковое 32-битное целое число.
              Int88-байтовое целое число со знаком, также известное как 64-битное целое число со знаком, может хранить значения в диапазоне от -9,223,372,036,854,775,808 до 9,223,372,036,854,775,807. Рекомендуется использовать его для представления типа i64 из ethereum.
              BigIntБольшие целые числа. Используются для типов uint32, int64, uint64, …, uint256 из Ethereum. Примечание: все типы, меньше чем uint32, такие как int32, uint24 или int8, представлены как i32.
              BigDecimalBigDecimal Высокоточные десятичные числа, представленные как мантисса и экспонента. Диапазон экспоненты от −6143 до +6144. Округляется до 34 значащих цифр.
              TimestampЭто значение типа i64 в микросекундах. Обычно используется для полей timestamp в временных рядах и агрегациях.

              Перечисления

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

              1enum TokenStatus {2  OriginalOwner3  SecondOwner4  ThirdOwner5}

              Как только перечисление определено в схеме, вы можете использовать строковое представление значения перечисления для установки поля перечисления в объекте. Например, вы можете установить tokenStatus в значение SecondOwner, сначала определив ваш объект, а затем установив поле с помощью entity.tokenStatus = "SecondOwner". Пример ниже демонстрирует, как будет выглядеть объект Token с полем перечисления:

              Более подробную информацию о написании перечислений можно найти в документации по GraphQL⁠.

              Связи объектов

              Объект может иметь связь с одним или несколькими другими объектами в Вашей схеме. Эти связи могут быть использованы в Ваших запросах. Связи в The Graph являются однонаправленными. Можно смоделировать двунаправленные связи, определив однонаправленную связь на любом “конце” связи.

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

              Связи “Один к одному”

              Определите тип объекта Transaction с необязательной связью “один к одному” с типом объекта TransactionReceipt:

              1type Transaction @entity(immutable: true) {2  id: Bytes!3  transactionReceipt: TransactionReceipt4}56type TransactionReceipt @entity(immutable: true) {7  id: Bytes!8  transaction: Transaction9}

              Связи “Один ко многим”

              Определите тип объекта TokenBalance с обязательной связью “один ко многим” с типом объекта Token:

              1type Token @entity(immutable: true) {2  id: Bytes!3}45type TokenBalance @entity {6  id: Bytes!7  amount: Int!8  token: Token!9}

              Обратные запросы

              Обратные поисковые запросы можно определить в объекте с помощью поля @derivedFrom. Это создает виртуальное поле в объекте, которое может быть запрашиваемо, но не может быть установлено вручную через API отображений. Вместо этого оно вычисляется на основе связи, определенной в другом объекте. Для таких отношений редко имеет смысл хранить обе стороны связи, и как производительность индексирования, так и производительность запросов будут лучше, если хранится только одна сторона связи, а другая извлекается.

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

              Пример

              Мы можем сделать балансы для токена доступными из токена, создав поле tokenBalances:

              1type Token @entity(immutable: true) {2  id: Bytes!3  tokenBalances: [TokenBalance!]! @derivedFrom(field: "token")4}56type TokenBalance @entity {7  id: Bytes!8  amount: Int!9  token: Token!10}

              Вот пример того, как написать мэппинг для субграфа с обратными поисковыми запросами:

              1let token = new Token(event.address) // Создание токена2token.save() // tokenBalances определяется автоматически34let tokenBalance = new TokenBalance(event.address)5tokenBalance.amount = BigInt.fromI32(0)6tokenBalance.token = token.id // Ссылка на токен сохраняется здесь7tokenBalance.save()

              Связи “Многие ко многим”

              Для связей “многие ко многим”, таких, например, как пользователи, каждый из которых может принадлежать к любому числу организаций, наиболее простым, но, как правило, не самым производительным способом моделирования связей является создание массива в каждом из двух задействованных объектов. Если связь симметрична, то необходимо сохранить только одну сторону связи, а другая сторона может быть выведена.

              Пример

              Определите обратный поиск от объекта User к объекту Organization. В примере ниже это достигается через поиск атрибута members внутри объекта Organization. В запросах поле organizations на объекте User будет разрешаться путем поиска всех объектов Organization, которые включают идентификатор пользователя.

              1type Organization @entity {2  id: Bytes!3  name: String!4  members: [User!]!5}67type User @entity {8  id: Bytes!9  name: String!10  organizations: [Organization!]! @derivedFrom(field: "members")11}

              Более эффективный способ хранения этих отношений — это использование таблицы отображений, которая содержит одну запись для каждой пары User / Organization с такой схемой

              1type Organization @entity {2  id: Bytes!3  name: String!4  members: [UserOrganization!]! @derivedFrom(field: "organization")5}67type User @entity {8  id: Bytes!9  name: String!10  organizations: [UserOrganization!] @derivedFrom(field: "user")11}1213type UserOrganization @entity {14  id: Bytes! # Set to `user.id.concat(organization.id)`15  user: User!16  organization: Organization!17}

              Этот подход требует, чтобы запросы опускались на один дополнительный уровень для получения, например, сведений об организациях для пользователей:

              1query usersWithOrganizations {2  users {3    organizations {4      # this is a UserOrganization entity5      organization {6        name7      }8    }9  }10}

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

              Добавление комментариев к схеме

              Согласно спецификации GraphQL, комментарии могут быть добавлены над атрибутами объектов схемы с использованием символа решетки #. Это показано в следующем примере:

              1type MyFirstEntity @entity {2  #уникальный идентификатор и первичный ключ объекта3  id: Bytes!4  address: Bytes!5}

              Определение полей полнотекстового поиска

              Полнотекстовые поисковые запросы фильтруют и ранжируют объекты на основе введенных данных текстового запроса. Полнотекстовые запросы способны возвращать совпадения по схожим словам путем обработки текста запроса в виде строк перед сравнением с индексированными текстовыми данными.

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

              Чтобы добавить полнотекстовый запрос, включите тип _Schema_ с директивой fulltext в схему GraphQL.

              1type _Schema_2  @fulltext(3    name: "bandSearch"4    language: en5    algorithm: rank6    include: [{ entity: "Band", fields: [{ name: "name" }, { name: "description" }, { name: "bio" }] }]7  )89type Band @entity {10  id: Bytes!11  name: String!12  description: String!13  bio: String14  wallet: Address15  labels: [Label!]!16  discography: [Album!]!17  members: [Musician!]!18}

              Пример поля bandSearch может быть использован в запросах для фильтрации объектов Band на основе текстовых документов в полях name, description и bio. Перейдите к GraphQL API - Запросы для описания API полнотекстового поиска и других примеров использования.

              1query {2  bandSearch(text: "breaks & electro & detroit") {3    id4    name5    description6    wallet7  }8}

              Управление функциями: Начиная с specVersion 0.0.4 и далее, fullTextSearch должен быть объявлен в разделе features манифеста субграфа.

              Поддерживаемые языки

              Выбор другого языка окажет решающее, хотя иногда и неуловимое влияние на API полнотекстового поиска. Поля, охватываемые полем полнотекстового запроса, рассматриваются в контексте выбранного языка, поэтому лексемы, полученные в результате анализа и поисковых запросов, варьируются от языка к языку. Например: при использовании поддерживаемого турецкого словаря “token” переводится как “toke”, в то время как, конечно, словарь английского языка переводит его в “token”.

              Поддерживаемые языковые словари:

              КодСловарь
              простойОбщий
              daДатский
              nlГолландский
              enАнглийский
              fiФинский
              frФранцузский
              deНемецкий
              huВенгерский
              itИтальянский
              noНорвежский
              ptПортугальский
              roРумынский
              ruРусский
              esИспанский
              svШведский
              trТурецкий

              Алгоритмы ранжирования

              Поддерживаемые алгоритмы для упорядочивания результатов:

              АлгоритмОписание
              rankИспользуйте качество соответствия (0-1) полнотекстового запроса, чтобы упорядочить результаты.
              proximityRankПохоже на рейтинг, но также учитывает близость совпадений.
              ⁠Редактировать на GitHub⁠

              Subgraph ManifestWriting AssemblyScript Mappings
              На этой странице
              • Обзор
              • Определение Объектов
              • Встроенные скалярные типы
              • Перечисления
              • Связи объектов
              • Обратные запросы
              • Добавление комментариев к схеме
              • Определение полей полнотекстового поиска
              • Поддерживаемые языки
              • Алгоритмы ранжирования
              The GraphСтатусТестовая сетьБрундовые ресурсыФорумБезопасностьПолитика конфиденциальностиУсловия обслуживания