-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support recursive relationships #3725
Comments
For data structures like this, The best way I've found to handle with GraphQl in general (unrelated to prisma) is to flatten the tree to one level, and track the parentId/index etc as properties. Then have a simple/fast recursive function that builds the tree or converts back to a flat list etc. This is more of a Graphql limitation (not supporting recursion) but I've found working with a flattened list vs (n)tree has other benefits, like being able to mutate the tree as a linked list/doubly linked list etc). |
Self referencing relations are possible in the Prisma schema today. See docs on them here: https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-schema/relations#self-relations That can get you started. But in general thanks for opening this issue for For now you can also use a raw query here: https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-client/raw-database-access |
Thanks @pantharshit00 . Thankfully this isn't required in my project right now but is in a roadmap for another personal project so thought I'd bring it up. Thanks again |
I have used raw queries for recursive queries. |
Couldn't a possible solution be having a decorator in our model saying that a certain relation is included by default when we query that table? So that when we would query for the tree structure, we would only query for the rows that have the parentId as null and the children would come by default, even in the children's children. |
Hey guys, I'm struggling with the same issue, and would really like to see this feature. Someone on StackOverflow suggested that I leave a comment here to help the developers track the demand for this feature. Meanwhile, can someone please help me to figure out how to write a raw query that accomplishes the same thing? I'm not too great at SQL, and I'm relatively new to Prisma, and I'd really love to find a solution to this. |
I've been waiting for this feature too. It would be awesome to see this feature gain some traction. |
If the Prisma team is looking for a real-world example then I have one! Here's a schema with a And here's a recursive CTE used to count the number of replies to threads (posts with "first" set to true) If this could be expressed purely via Prisma, that would be awesome! I already hate the complexity and lack of composability with SQL syntax, but recursive CTEs are a special kind of hell! |
+1 |
This is definitely the best way to handle, in my opinion. You can refold the items on the client or server with a simple recursive for loop. This (incomplete?) example may have some unnecessary steps for your need, but it's taken from code that takes an ordered, flat array of React/DOM components (json) and builds a correct DOM/component tree using a parent id stored on each component. It could also be adapted to track an index property, if the array needed to be stored unordered. const children = []
const treeDict = {}
const childrenNested = generateTree(children, treeDict)
function generateTree(ch, dict) {
const tree = []
for (
let i = 0, children, parent, node, pid, id, n = ch.length;
i < n;
++i
) {
node = ch[i]
id = node.id
pid = node.pid
if (pid === null || !(typeof pid === 'string' && pid.length > 0)) {
tree[tree.length] = dict[id]
continue
}
parent = dict[pid]
children = parent.children
if (children === null || children === undefined) {
parent.children = [dict[id]]
continue
}
children[children.length] = dict[id]
}
return tree
} |
We also have recursive relationships in our database, similar to nodes having potentially one parent and potentially many children. As we grow features to process data across nodes at depth it's becoming a performance hit to use Prisma because we need to continuously query, check for more nodes and query each of those again, etc. Is there any plan for this to be on the roadmap at any point in the next 6 months? Thanks in advance! |
+1 |
Bumping this as the issue was opened a year ago. For what is a pretty basic feature I would expect more traction from the Prisma team? |
Faced similar situation like this. Ended up writing a helper function just to build up the category object. Probably wasting performance on querying but it solves the problem. Here is the sample code. The data structure goes as follow: import prisma from '../../../lib/prisma'
async function GetSubCategory(arr) {
const new_arr = []
for (const element of arr) {
const subCategories = await prisma.category.findMany({
where: {
parentCategoryId: element.id
},
include: {
documents: true
}
})
if (!Array.isArray(subCategories) || !subCategories.length) {
// array does not exist, is not an array, or is empty
// ⇒ do not attempt to process array
new_arr.push(element)
} else {
const temp_arr = await GetSubCategory(subCategories)
element.subCategories = temp_arr
new_arr.push(element)
}
}
return new_arr
}
export default async function handler(req, res) {
const session = await unstable_getServerSession(req, res, authOptions)
const project = await prisma.user.findUnique({
where: {
email: session.user.email
}
}).projects({
where: {
id: req.query.pid
},
include: {
categories: {
include: {
documents: true,
}
}
}
})
project[0].categories = await GetSubCategory(project[0].categories)
if (!project[0]) {
res.status(404).json({ message: "Project not found." });
return;
}
return res.json(project[0])
} |
+1 |
+1 I'm surprised recursive querying for self referencing models aren't support for Prisma. Even simple features like nested folders is commonly expected for modern applications. Given there is underlying DB support for this, I'm curious why this hasn't been prioritized? |
Error validating: A self-relation must have I needed up doing it that why: ofc i can't query the depth of each subfolder :/ model Folder { name String |
+1 Waiting for this! |
This comment was marked as abuse.
This comment was marked as abuse.
I would propose something like a special "recurse" option on the include interface. Ideally it could be "true" for infinite recursion, or a number if I want to specify a max recursion depth. For example: Infinite recursion:
Max depth:
This feels like it shouldn't be too hard to create, and it would be such a great feature. |
Any Info about a potential release of the feature ? Kind of needed and a big time saver too. One of the main reason to use ORM too 🤔 ( however everything is incredible down here thanks a lot prisma team ❤️ ) |
This is orthogonal to the need for recursive queries...you'll get flat output from the database and reconstruct the tree in JS regardless, but if the table is large you can't afford to fetch everything, you have to select only the rows you want and then use a recursive query to fetch their ancestors and/or descendants, and the tree-building function doesn't help with that |
Closing in on 3 years, is this on any roadmap? |
@AlexanderHott here is Prisma roadmap and sadly there is no mention of this feature as long as no feedback from the team here too |
I also have the posts -> comments -> comments use case. But another use case I have is for the permissions system I am building. Each roles will have a set of permissions, but they can also inherit permissions of other roles, like in this example (imagine foreign keys instead of strings): const roles = [
{
name: "user",
permissions: ["services.read"]
},
{
name: "moderator",
permissions: ["services.create"],
inherit: ["user"]
},
{
name: "admin",
permissions: ["services.edit", "services.delete"],
inherit: ["moderator"]
},
] If a user has role admin, I would need a recursive query and then i will gather all his permissions with javascript // prisma returned object:
const exampleUser = {
username: "blah",
permissions: ["random.read", "random.create", "random.edit"], // specific permissions to user
permissionsDenied: ["services.delete"], // negate permissions (highest precedence)
roles: [
{
name: "admin",
permissions: ["services.edit", "services.delete"],
inherit: [
{
name: "moderator",
permissions: ["services.create"],
inherit: [
{
name: "user",
permissions: ["services.read"]
}
]
}
]
}
]
} and hopefully it would take care of edge cases like having circular reference would stop the recursion |
possible syntax for above const user = await prisma.user.findUnique({
where: {
id: 99,
},
include: {
roles: {
recursiveInclude: {
inherit: true
}
// ^ same as include { inherit: { include: inherit ...}}
}
}
}) |
Dear Prisma developers, I don't like writing comments like "+1" but please do not ignore this feature request. I've been searching this topic occasionally for about a year incase I missed an issue related to it. |
This comment was marked as off-topic.
This comment was marked as off-topic.
Are there any updates on this? The proposed |
For us we not only would want to We are hacking this with a hard-coded "depth" parameter for now, but it's not very performant and breaks when the depth goes beyond that. |
Will this been add in Roadmap? Please do not ignore this feature request! |
Yes, one day. Right now we are busy with other things. We only have limited capacity, so can not just add all the things we find useful to a roadmap - otherwise none of them will get done. |
Nah... We need this |
Yes that's kinda of shame that we don't have it... |
As I suspected, this messes up the TS types for the model but I used it to generate the unwieldy object for the query. |
Wouldn't you be better of simply getting all categories and building the hierarchical structure post-retrieval? Would be more performant, and you could have a variable number of nesting levels |
Effectively, another challenge when using On my side I created a logic able to do that, to create a recursive function and keep my typing updated Here is my solution: export const getVotes = (auth?: SignedInAuthObject | SignedOutAuthObject) => {
return {
...(auth?.userId
? {
upVotes: { where: { userId: auth.userId } },
downVotes: { where: { userId: auth.userId } },
}
: {}),
_count: { select: { upVotes: true, downVotes: true } },
};
};
// ...
// I created a recursive function and defined the return type
export interface RecursivePost extends ReturnType<typeof getVotes> {
comments?: {
orderBy: { createdAt: "desc" | "asc" };
include: RecursivePost | ReturnType<typeof getVotes>;
};
}
export const generateNestedComments = (
depth: number,
auth?: SignedInAuthObject | SignedOutAuthObject,
): RecursivePost => {
if (depth === 0) return getVotes(auth);
const include = generateNestedComments(depth - 1, auth);
return {
...getVotes(auth),
comments: {
orderBy: { createdAt: "desc" },
include,
},
};
}; 💡 In my shared code I'm getting a Post and a nested list of comments on that post, and because I was drunk and lazy I used the same Here are the code source links:
Agreed, my code is not the best one and it would be much easier with a built-in solution, but still, my solution helped me with this Reddit-clone app, (here's the link https://reddit-clone-nine-xi.vercel.app/) |
Getting all the records is not expensive if say, there's a global category tree and all users need the whole tree. However, it might be more complicated than that and you could end up with lots of irrelevant categories. This gave me an 💡: you can limit access to records with Zenstack so that you only get the relevant categories. This could be really useful if say a category could exist in multiple trees via a many-to-many relationship. However, Zenstack doesn't work with recursion either so I ended up with some ugly rules to deal with recursion e.g. :
There's a somewhat related issue here: zenstackhq/zenstack#276 |
Is there an update to this feature request? |
+1 |
Idk if u made it but u can try to PS : saw after writing that people already said it but just pinging u trying to help :) BUT PLEASE PRISMA TEAM DO IIIIIIITTTTTTT ( still love ya ) |
Problem
Let's say I have a table called
categories
. This table can refer to itself as a foreign key withparentCategory
.So a categories can have many subcategories, and subcategories can also have subcategories, with an unlimited combination. The idea is to support recursive queries to easily query them along with their sub categories.
Suggested solution
Some valid implementation of something similar to Postgres
WITH RECURSIVE
https://www.postgresqltutorial.com/postgresql-recursive-query/
Alternatives
N/A
Additional context
N/A
The text was updated successfully, but these errors were encountered: