Комментарии (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>;
}
Хорошая фильтрация на бэкенде делает фронтенд более быстрым и отзывчивым, потому что меньше данных загружается и обрабатывается в браузере.