← Назад к вопросам

Как реализовать фильтрацию в Prisma?

2.0 Middle🔥 191 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Фильтрация в Prisma ORM

Хотя Prisma в основном используется в бэкенде (Node.js), хороший фронтенд разработчик с полнослеко навыками должен понимать как работает фильтрация на базе данных. Это важно для оптимизации API запросов.

Базовая фильтрация в Prisma

// prisma/schema.prisma
model Question {
  id        String   @id @default(cuid())
  title     String
  text      String
  status    String   @default("ACTIVE") // ACTIVE, ARCHIVED
  profession   Profession @relation(fields: [professionId], references: [id])
  professionId String
  createdAt DateTime @default(now())
  comments  Comment[]
}

model Comment {
  id        String @id @default(cuid())
  content   String
  question  Question @relation(fields: [questionId], references: [id])
  questionId String
}

Теперь реализуем фильтрацию:

// backend/api/questions.ts
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

// Базовая фильтрация
async function getQuestions(professionId: string) {
  return prisma.question.findMany({
    where: {
      professionId: professionId,
      status: 'ACTIVE',
    },
  });
}

// Фильтрация по нескольким полям
async function getQuestionsByTitle(title: string) {
  return prisma.question.findMany({
    where: {
      title: {
        contains: title, // содержит текст
        mode: 'insensitive', // case-insensitive поиск
      },
    },
  });
}

// Фильтрация с OR логикой
async function searchQuestions(query: string) {
  return prisma.question.findMany({
    where: {
      OR: [
        { title: { contains: query, mode: 'insensitive' } },
        { text: { contains: query, mode: 'insensitive' } },
      ],
    },
  });
}

// Фильтрация с AND логикой
async function getActiveProfessionQuestions(professionId: string) {
  return prisma.question.findMany({
    where: {
      AND: [
        { professionId: professionId },
        { status: 'ACTIVE' },
      ],
    },
  });
}

Расширенные фильтры

// Фильтрация по диапазону (dates, numbers)
async function getRecentQuestions(daysAgo: number) {
  const since = new Date();
  since.setDate(since.getDate() - daysAgo);
  
  return prisma.question.findMany({
    where: {
      createdAt: {
        gte: since, // greater than or equal
      },
    },
  });
}

// Фильтрация: НЕ равно
async function getNonArchivedQuestions() {
  return prisma.question.findMany({
    where: {
      status: {
        not: 'ARCHIVED',
      },
    },
  });
}

// Фильтрация по наличию отношений
async function getQuestionsWithComments() {
  return prisma.question.findMany({
    where: {
      comments: {
        some: {}, // есть хотя бы один комментарий
      },
    },
  });
}

// Фильтрация по количеству отношений
async function getQuestionsWithManyComments(minComments: number) {
  return prisma.question.findMany({
    where: {
      comments: {
        some: {}, // вложенная фильтрация
      },
    },
    include: {
      comments: true,
    },
  });
  // затем отфильтровать в приложении или использовать Raw SQL
}

// IN фильтр (выбрать из списка)
async function getQuestionsFromProfessions(professionIds: string[]) {
  return prisma.question.findMany({
    where: {
      professionId: {
        in: professionIds, // IN ('id1', 'id2', 'id3')
      },
    },
  });
}

// NIN фильтр (исключить из списка)
async function getQuestionsExcludeProfessions(excludeProfessionIds: string[]) {
  return prisma.question.findMany({
    where: {
      professionId: {
        notIn: excludeProfessionIds,
      },
    },
  });
}

Комплексная фильтрация с типизацией

// types/filters.ts
interface QuestionFilter {
  professionId?: string;
  status?: 'ACTIVE' | 'ARCHIVED';
  searchQuery?: string;
  createdFrom?: Date;
  createdTo?: Date;
  limit?: number;
  offset?: number;
}

// backend/services/questionService.ts
async function getQuestionsFiltered(filter: QuestionFilter) {
  const where: any = {};
  
  if (filter.professionId) {
    where.professionId = filter.professionId;
  }
  
  if (filter.status) {
    where.status = filter.status;
  }
  
  if (filter.searchQuery) {
    where.OR = [
      { title: { contains: filter.searchQuery, mode: 'insensitive' } },
      { text: { contains: filter.searchQuery, mode: 'insensitive' } },
    ];
  }
  
  if (filter.createdFrom || filter.createdTo) {
    where.createdAt = {};
    if (filter.createdFrom) {
      where.createdAt.gte = filter.createdFrom;
    }
    if (filter.createdTo) {
      where.createdAt.lte = filter.createdTo;
    }
  }
  
  return prisma.question.findMany({
    where,
    skip: filter.offset || 0,
    take: filter.limit || 20,
    orderBy: {
      createdAt: 'desc',
    },
  });
}

// API endpoint (FastAPI пример)
@router.get('/api/v1/questions')
async def get_questions(
  profession_id: Optional[str] = None,
  status: Optional[str] = None,
  search: Optional[str] = None,
  limit: int = 20,
  offset: int = 0,
):
  filter = QuestionFilter(
    professionId=profession_id,
    status=status,
    searchQuery=search,
    limit=limit,
    offset=offset,
  )
  return await questionService.getQuestionsFiltered(filter)

Оптимизация: включение связанных данных

// Получить вопросы с комментариями
async function getQuestionsWithComments(professionId: string) {
  return prisma.question.findMany({
    where: {
      professionId: professionId,
      status: 'ACTIVE',
    },
    include: {
      comments: true, // загрузить все комментарии
    },
  });
}

// Получить с фильтрацией комментариев
async function getQuestionsWithRecentComments() {
  return prisma.question.findMany({
    include: {
      comments: {
        where: {
          createdAt: {
            gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000), // последние 7 дней
          },
        },
        orderBy: {
          createdAt: 'desc',
        },
        take: 5, // только последние 5 комментариев
      },
    },
  });
}

Raw SQL когда Prisma недостаточно

// Сложный SQL запрос когда Prisma не может это выразить
async function getQuestionsWithCommentCount() {
  return prisma.$queryRaw`
    SELECT 
      q.id,
      q.title,
      COUNT(c.id) as commentCount
    FROM Question q
    LEFT JOIN Comment c ON q.id = c.questionId
    GROUP BY q.id
    HAVING COUNT(c.id) > 0
    ORDER BY commentCount DESC
  `;
}

Фронтенд: как строить фильтры для API

// frontend/hooks/useQuestionsFilter.ts
import { useSearchParams } from 'next/navigation';

export function useQuestionsFilter() {
  const searchParams = useSearchParams();
  
  const filter = {
    professionId: searchParams.get('profession') || undefined,
    status: searchParams.get('status') || undefined,
    search: searchParams.get('q') || undefined,
    limit: Number(searchParams.get('limit')) || 20,
    offset: Number(searchParams.get('offset')) || 0,
  };
  
  return filter;
}

// Использование в компоненте
function QuestionsList() {
  const filter = useQuestionsFilter();
  const { data: questions } = useGetQuestionsFiltered(filter);
  
  return <div>{/* render questions */}</div>;
}

Хорошая фильтрация на бэкенде делает фронтенд более быстрым и отзывчивым, потому что меньше данных загружается и обрабатывается в браузере.

Как реализовать фильтрацию в Prisma? | PrepBro