Tree structures support
See original GitHub issueProblem
Hello! There is no real functionality for working with tree structures recursively.
It would be awesome if there was a functionality added to define a model & query the most popular hierarchal data structures for relational databases. But for the sake of simplicity and referential integrity, i think the adjacency list support would be the the best choice for the first solid implementation.
Suggested solution
The first thing that comes to my mind is that there needs to be some kind of way to declare that a model inside a schema wants to be a tree node.
Simple example:
model Category TreeNode {
// Or: Tree, TreeNode('adjacencyList'), Tree('adjancencyList')
id Int @id @default(autoincrement())
name String
// Those fields below could be added behind the scenes based on model declaration above
// But that would be dependent on experimental schema migration tool, so idk
// Anyways, in the case of basic adjacency list, those could be required
children Category[] @relation("CategoryToCategory")
parent Category? @relation("CategoryToCategory", fields: [parentId], references: [id])
parentId Int?
}
Then there should be some methods for working with trees. Some basic ones, as an example:
- getDescendants
- getAncestors
- getChildren
- getRoot or getRoots
- etc…
Some of the methods would use recursive CTE queries in the case of adjacency list, and other algorithms in case of other tree structures, if implemented.
Then it would work like this:
const subCategories = await prisma.category
.findUnique({
where: { id: 5 }
})
.getDescendants({
includeSelf: true // Optional, and maybe default as false? Just an idea.
// ...and other useful properties, depending on the method...
})
// Or
const subCategories = await prisma.category
.getDescendants({
where: { id: 5 }
})
Additional context
Here are some design ideas: https://typeorm.io/#/tree-entities https://github.com/django-treebeard/django-treebeard https://django-mptt.readthedocs.io/en/latest/models.html#
Issue Analytics
- State:
- Created 3 years ago
- Reactions:92
- Comments:12 (1 by maintainers)
Top GitHub Comments
Context
Related issues: #3725 and #3643
A popular application that uses this pattern is Notion.
Prisma already supports the data structure required for modeling a tree, known as one-to-many self relation.
What is missing is better query support. I don’t believe we need to introduce one or more special model types in order to achieve this.
Problem
The schema
currently supports queries like this to load the children of a category. You can extend this to load as many levels of children you want, and it will be returned in a correctly typed object structure:
And similarly to retrieve parents:
While this works, you must specify a maximum depth, and you have to do it in this rather clunky way. It would be nice if you could perform these same queries without specifying a maximum depth, and optionally specify a maximum depth simply by providing an integer.
Suggestion
For models with self relations, we could introduce a new
includeRecursive
query:If the Category with id
1
is the root, this query will load the entire tree.Similarly, if you need to load a category, and the id of all parent categories all the way up to the root, you could write this query:
A consideration
This query will return a structure like the following:
This is conceptually easy to understand, and fits the mental model that Prisma generally uses, but can be a bit unwieldy. Perhaps we could introduce a convenience method to flatten the recursive relation:
Implementation
This could use the same multiple-roundtrips implementation that our current
include: {children: true}
query does. I believe this is the strategy Notion is using to load nested blocks on a page.Alternatively, we could use the
recursive CTE
approach mentioned in the parent. It looks like all common SQL servers (and definitely MySQL, Postgres, MSSQL and SQLite) support recursive CTEs.Either way, we would need to take care not to end up in an infinite loop, either by enforcing a maximum depth or employing a strategy like the one described here. Max depth could be exposed in the API like this:
Further work
It would be great if there was official Prisma support for this. Are there any plans on the roadmap for this?