Relations
Define queryable relationships between models and other models or accounts.
Types of Relations
There are a few primary forms of relations currently supported by ComposeDB:
- Account to model relations
- Model to account relations
- Model to model relations
- Account to account relations
Account to Model
Account to model relations enable linking and querying data to the account that created it. By default the @createmodel
directive (used when creating a new model) requires that every model must specify a relation to its author’s account. This was covered in Writing Models.
Example: Simple Profile
Here’s a model for a very simple user profile that can be queried based on the author:
# Define simple profile model
# Relate it to the author's account
# Limit to one profile per account
# Enable queries based on author
type SimpleProfile @createModel(accountRelation: SINGLE, description: "Very basic profile") {
displayName: String! @string(minLength: 3, maxLength: 50)
}
Where:
accountRelation
relates the profile to the author’s accountSINGLE
limits to one profile per account
Model to Account
Model to account relations enable you to link data to and query data from an account other than the data’s author. When using this type of relation, you need to define a model field that stores an account (e.g. a DID), then add the @accountReference
directive to make it queryable.
Example: Direct message (DM)
Here’s a model for a user-to-user message that can be queried based on the recipient:
# Define message model
# Relate it to author's account
# Allow unlimited sent messages
# Store reference to recipient's account
# Enable queries based on recipient
type Message @createModel(accountRelation: LIST, description: "Direct message model") {
recipient: DID! @accountReference
directMessage: String! @string(minLength: 1, maxLength: 200)
}
Where:
accountRelation
relates the message to the author’s accountLIST
allows unlimited messagesrecipient
references the recipient’s account by storing itsDID!
, using@accountReference
Model to Model
Model to model relations enable you to link data to and query data from another piece of data. These relations can be uni-directional (e.g. query a post from a comment) or bi-directional (e.g. query a post from a comment and query all comments from a post).
There is type of model-to-model relation that includes the user as part of the relationship. This is achieved by using the SET
account relation type, which allows users to enforce a constraint where each user account (or DID) can create only one instance of a model for a specific record of another model.
Example: Post with comments and likes
Here’s a model that allows many comments from the same or different account to be made on a single post. It supports unlimited comments per user, and bi-directional queries from any comment or like to the original post and from the original post to all of its comments and likes. The model schema also creates a relation between posts and likes enabling a single like per post by an account, meaning a single account will only be able to like the post once
# Load post model (using streamID)
type Post @loadModel(id: "kjzl6hvfrbw6c822s0cj1ug59spj648ml8a6mbqaz91wx8zx3mlwi76tfh3u1dy") {
id: ID!
}
# New comment model
# Set reference to original post
# Enable querying comment to get original post
type Comment @createModel(accountRelation: LIST, description: "A comment on a Post") {
postID: StreamID! @documentReference(model: "Post")
post: Post! @relationDocument(property: "postID")
text: String! @string(maxLength: 500)
}
# New like model
# Set relationship to original post
# Enable querying comment to get original post
type Like @createModel(description: "A like on a post", accountRelation: SET, accountRelationFields: ["postID"]) {
postID: StreamID! @documentReference(model: "Post")
post: Post! @relationDocument(property: "postID")
}
Relations can also be created between models loaded from known streamIDs
# Load comment model
type Comment @loadModel(id: "kjzl6hvfrbw6c9oo2ync09y6z5c9mas9u49lfzcowepuzxmcn3pzztvzd0c7gh0") {
id: ID!
}
# Load post model
# Extend post model with comments and likes
# Set relationships to all comments and likes
# Enable querying post to get all comments and likes
type Post @loadModel(id: "kjzl6hvfrbw6c822s0cj1ug59spj648ml8a6mbqaz91wx8zx3mlwi76tfh3u1dy") {
comments: [Comment] @relationFrom(model: "Comment", property: "postID")
likes: [Like] @relationFrom(model: "Like", property: "postID")
}
Where:
id
is a simple placeholder, since empty types are not allowedpostID
defines the relationship from a comment to the original post, using@documentReference
post
allows accessing the original post from the comment, using@relationDocument
text
defines a string for the commentcomments
defines the relationships from a post to a collection of comments, using@relationFrom
; requires specifying the model relation (Comment
) and the specific property that stores the relation (postID
)likes
defines the relationships from a post to a collection of comments, using@relationFrom
; requires specifying the model relation (Like
) and the specific property that stores the relation (postID
)
Using interfaces
When defining relations, it is possible to reference model interfaces to allow for a wider range of documents in the relations set, for example to create a collection of documents using different models implementing the same interface:
interface TextContent @createModel(description: "Required text content interface") {
text: String! @string(maxLength: 10000)
}
type Page implements TextContent @createModel(description: "Page model") {
title: String @string(maxLength: 100)
text: String! @string(maxLength: 10000)
}
type Post implements TextContent @createModel(description: "Post model") {
title: String! @string(maxLength: 100)
text: String! @string(maxLength: 10000)
createdAt: DateTime!
}
type ContentCollectionItem
@createModel(description: "Association between a collection and an item") {
# The Node interface is used here instead of ContentCollection, see warning below
collectionID: StreamID! @documentReference(model: "Node")
collection: Node! @relationDocument(property: "collectionID")
itemID: StreamID! @documentReference(model: "TextContent")
item: TextContent! @relationDocument(property: "itemID")
}
type ContentCollection @createModel(description: "Collection of text contents") {
name: String @string(maxLength: 50)
items: [ContentCollectionItem]!
@relationFrom(model: "ContentCollectionItem", property: "collectionID")
}
ComposeDB does not support creating relations with circular references, such as ContentCollection
-> ContentCollectionItem
-> ContentCollection
in the example above.
To work around this limitation, it is possible to use the Node
interface as a placeholder for any model. The example above uses the Node
interface instead of ContentCollection
to reference the collection in the ContentCollectionItem
in order to avoid creating a circular reference.
Account to Account
Account to account relations are on the roadmap, but not yet supported.
Account to account relations enable you to define a relationship between an account and a different account, and query both ways based on that relationship. This is useful for creating structures such as social graphs where the relationship represents a follow.
Next Steps
Now that you understand the fundamentals of creating models with different types of relations, let's create a composite so we can use it in our app.