type _Schema_
@import(
types: ["Address", { name: "Account", as: "EthAccount" }],
from: { id: "Qmsubgraph" })
A string entry in types
is shorthand for { name: STRING, as: STRING }
Types that are explicitly imported are available in the schema under the
name mentioned in as
. All other types are imported by prefixing their
name with the subgraph ID. The implementation of the import process must
take care to ensure that nested imports do not lead to multiple definitions
of the same type in the importing subgraph.
The as
names of types are treated as if the importing schema contained
type definitions for them, and the schema is validated accordingly.
Only types that are explicitly defined in a schema can be imported. It is not possible to import types from a schema that itself imports that type.
For example, if the subgraph we are importing from above has the GraphQL schema
type Address {
id: ID!
owner: User!
}
type User {
id: ID!
name: String!
}
type Account {
id: ID!
owner: User!
}
then the import statement above has the same effect as putting the following definitions into the GraphQL schema of the importing subgraph; we also add directives to help keep track of where things come from.
type Address @imported(from: "Qmsubgraph", type: "Address", direct: true) {
id: ID!
owner: I01_User!
}
type Qmsubgraph_User @imported(from: "Qmsubgraph", type: "User", direct: false) {
id: ID!
name: String!
}
type EthAccount @imported(from: "Qmsubgraph", type: "Account", direct: true) {
id: ID!
owner: I01_User!
}
The @imported
directive always points to the subgraph that actually
stores the data for that type. The direct
attribute indicates whether
this type is directly imported and can therefore be queried through the
GraphQL API.
Anywhere where we inject @subgraphId
directives today, we will use the id
of the source subgraph if the type has an @imported
directive.
The API schema is derived from the input schema after expanding imports in
the usual way, except that only imported types with a direct: true
annotation are turned into attributes of the Query
and Subscription
root types.
We carry out the expansion of imports very early on in our schema
processing (where exactly TBD) The expansion requires acces to a Store
since we need to look up the input schema of the imported
subgraphs. Because those input schemas will have their imports expanded
already, no recursion is needed to deal with nested imports. The expansion
simply keeps existing @imported
directives, and adds new ones to the
types that don’t have one yet.
When constructing the Layout
, we only generate tables for types that do
not have an @imported
annotation.
When deploying a composed subgraph, all its components must already be deployed. It is a deploy time error if they are not.
A composed subgraph will keep all its components assigned. Today, a deployment will be assigned if it is the current or pending version of a subgraph. This condition will need to be pushed down transitively to all component subgraphs. In other words, a deployment will be assigned if it is the current or pending version of a subgraph, or transitively used as an import by the current or pending version of a subgraph.
To facilitate this logic, we will explicitly track the relationship between
composed subgraphs and their components by adding an
imports
field to SubgraphDeployment
:
type SubgraphDeployment {
id: ID!
imports: [SubgraphDeployment!]!
.. other fields we have today ..
}
The list of active subgraphs, i.e., subgraphs that should be assigned can be generated with the following SQL query:
with recursive active(id) as (
-- base case: current and pending versions
select d.id
from subgraph_deployment d
where exists (select 1
from subgraph s, subgraph_version v
where d.id = v.deployment
and v.id in (s.current_version, s.pending_version))
union
-- iteration: add imports of active subgraphs
select unnest(d.imports)
from subgraph_deployment d, active a
where d.id = a.id)
select id from active
This change in assignment rules will require changes to the assignment
machinery in core::subgraph::registrar::create_subgraph_version
and
Store.reconcile_assignments
That part of the implementation is
nontrivial, and is complicated by the fact that it needs to compute changes
to assignments based on metadata changes that are not reflected in the
database yet.
Query execution right now does not access tables from more than one subgraph. That will change ones composed subgraphs can define interfaces and implement them on imported types.
As such, for now query execution simply needs to pick the right subgraph
and actual type name when it constructs the EntityQuery
in
graphql::store::query::build_query
: when a type has an @imported
annotation, use the subgraph and type name given in the @imported
annotation, otherwise use the @subgraphId
and the given type name.