Hey guys, I'm currently going insane `const name ...
# orm-help
d
Hey guys, I'm currently going insane
const name = _input_.name ?? undefined;
   
const categories = _input_.categories ?? undefined;
   
const games = await prisma.game.findMany({
     
where: {
       
AND: {
         
name: { contains: name, mode: 'insensitive' },
         
categories: {
           
some: {
           
}
         
}
       
}
     
},
I have this query and I'm trying to to check if any category matches one of the categories array (input) how do I map this?
s
Are you trying to find any
game
matching the provided name and where it has a category in a list of categories provided? If so, give something like this a try:
Copy code
const name = input.name ?? undefined;
const categories = input.categories ?? undefined;

const games = await prisma.game.findMany({
  where: {
    AND: [
        { name: { contains: name, mode: "insensitive" }},
        { categories: {
          some: {
            name: { in: categories.map( category => category.name) },
          },
        }}
    ]
  },
});
👍 1
d
kindof. Firstly I wanted to figure out how this works but now I would like to find any game containing ALL categories provided. The game can have more but it has to have the ones provided (all of them). So some or every won't suffice. Anything I can do?
s
Copy code
const name = input.name ?? undefined;
const categories = input.categories ?? undefined;

const games = await prisma.game.findMany({
  where: {
    AND: [
        { name: { contains: name, mode: "insensitive" }},
        { categories: {
          every: {
            name: { in: categories.map( category => category.name) },
          },
        }}
    ]
  },
});
So this should work by just switching to that
every
key. This is saying "give me all the games where every category in the provided list is associated with the game and where the name matches"
So the
every
key isn't saying the game has to only have the provided categories. It's saying every category provided should exist on the game. There could be more potentially and it should still match
d
I dont know if I'm doing something wrong but I ran this now
Copy code
const games = await prisma.game.findMany({
      where: {
        AND: [
          { name: { contains: name, mode: 'insensitive' } },
          { categories: {
            every: {
              description: { in: categories.map( (category) => category) },
            },
          }},
        ]
      },
      take: limit + 1,
      cursor: cursor ? { id: cursor } : undefined,
      include: {
        categories: true,
        genres: true
      },
      orderBy: {
        id: 'asc'
      }
    });
And I only get games where the input and the games categories exactly align so if my input is Single-player but the game itself has more categories it's not getting returned
s
Actually yes, sorry, I misunderstood.
Copy code
const name = input.name ?? undefined;
    const categories = input.categories ?? undefined;

    const games = await prisma.game.findMany({
      where: {
        AND: [
          { name: { contains: name, mode: 'insensitive' } },
          {
            categories: {
              name: {
                some: {
                  OR: categories.map(category => ({ name: category.name })),
                },
              },
            },
          },
        ],
      },
    });
Could you try this?
d
this doesnt work because it doesnt let me put description (you used name) before some. after opening categories: I can only use Every, Some or none
Copy code
model Game {
  id                String     @id
  name              String
  is_free           Boolean
  short_description String
  header_image      String
  website           String
  developers        String[]
  publishers        String[]
  windows           Boolean
  mac               Boolean
  linux             Boolean
  metacriticScore   Int
  metacriticUrl     String
  categories        Category[]
  genres            Genre[]
  background        String
  storeUrl          String
}

model Category {
  id          String @id
  description String
  games       Game[]
}

model Genre {
  id          String @id
  description String
  games       Game[]
}
that's the model
s
Copy code
const games = await prisma.game.findMany({
  where: {
    AND: [
      { name: { contains: name, mode: 'insensitive' } },
      {
        categories: {
          some: {
            OR: categories.map(category => ({ description: category })),
          },
        },
      },
    ],
  },
});
Ah okay, sorry working without your schema generated so I think I messed up the syntax
That above should be okay. Looking for any game where the categories includes the description from your list of descriptions (using OR operator)
d
this returns the game if it has one of the specified categories but not all of the specified
s
Switch the OR to AND?
d
then none get returned
running this with AND only returns games if I only specify one category, if I specify more it returns nothing
s
Did this solution work for you?
Copy code
where: {
  AND: [
    ...categories!.map((value) => ({ categories: { some: { description: { contains: value } } } })),
  ]
},
Saw this in the main chat
d
Yes it does but I can't believe this is how you are. supposed to solve this. in my opinion it isnt very readable and it confused me that mongodb has a hasEvery which would solve this without this confusing syntax
s
I agree, the hasMany syntax is a lot easier. It could be cleaned up a bit like
Copy code
where: {
  AND: categories.map(description => ({ categories: { some: { description: { contains:description } } } }))
}
d
there is another 'problem' with this. using the .map() makes it so you can't use mode: 'insensitive' anymore since you'll get a type-error. I already created an issue for this.
it seems like mode does work with your cleaned variant
s
Sweet, was just about to post a snippet of it working 👍
That way should keep the types valid
If that looks too hefty in your code, you could always split out that map into a filter generator function like so:
Copy code
const generateDescriptionFilter = descriptions => 
  descriptions.map(description => ({ categories: { some: { description: { contains:description } } } }))
This obviously doesn't change the funcionality, just cleans up the actual query if you prefer it that way
I tend to create more generalized query generators like this to compose my queries
d
the generator idea is great but this makes the mode inavailable again and you get the error type 'string' is not assignable to type 'QueryMode | undefined'
s
Yeah, you'd have to add the proper input/return types to the generator function using prisma's
Prisma
namespace to pull out your required types
d
I got it with defining an interface as return type
s
Sweet!
d
is it possible to share this generate function somehow between columns with a similar structure like lets say categories and genres or do i have to copy paste the function?
s
You could definitely get crafty with it. A more "generalized" version of the one above would be something like:
Copy code
const generateRelationSomeFilter = (list, relationName, column) => 
  list.map(value => ({ [relationName]: { some: { [column]: { contains: value } } } }))
Of course you could play around with it and make the search shape dynamic too
d
got this
Copy code
const generateRelationFilter = (list: string[], relationName: string, column: string) => list.map((value) => ({ [relationName]: { some: { [column]: { contains: value, mode: 'insensitive' } } } }));
running like this:
{ AND: generateRelationFilter(categories, 'categories', 'description') },
and it works like a charm
I'm not 100% satisfied with this but I guess beggars can't be choosers and it's a pretty nice solution
s
Awesome! Yeah a
hasMany
function in relation filters would be 👌 But for now that looks pretty good 🙂
d
yep thank you and @Maciek K for this solution. I created a feature request for a hasEvery for sql databases and I hope it get's implemented. Until then I'll work with this
👍 2