관계형 데이터에서 Prisma의 where 절은 자식 레코드 기준으로 부모를 거르는 필터를 제공함. 특히 some과 every는 겉보기엔 비슷하지만 결과 집합을 크게 바꾸는 핵심 차이가 있음. 단일 필드만으로 필터링해도 동일하지 않을 수 있어 주의 필요
개념
- some
- 관계된 레코드 중 적어도 하나가 조건을 만족하면 부모 포함
- 존재성 검사에 해당, 하나라도 매칭되면 true
- every
- 모든 관계된 레코드가 조건을 만족해야 부모 포함
- 단 하나라도 위배되면 제외됨
- 관계된 레코드가 하나도 없으면 vacuously true로 간주되어 조건 만족으로 처리됨
동작 원리
- some은 존재량화, every는 전칭량화에 해당함
- 차이는 자식 레코드가 0개이거나 2개 이상일 때 두드러짐. 1개일 때는 조건이 동일하다면 결과가 같아질 수 있음
- 특히 자식이 없는 경우 every는 기본적으로 참으로 평가되어 부모가 포함됨. 빈 결과를 제외하려면 추가 조건 필요
간단 예시
model Post {
id Int @id @default(autoincrement())
title String
comments Comment[]
}
model Comment {
id Int @id @default(autoincrement())
text String
postId Int
post Post @relation(fields: [postId], references: [id])
}
댓글 text가 ‘interesting’인 항목을 기준으로 게시글을 거르는 케이스를 가정
some 사용
const posts = await prisma.post.findMany({
where: {
comments: {
some: { text: "interesting" },
},
},
});
- 댓글 중 하나라도 텍스트가 ‘interesting’이면 포함됨
every 사용
const posts = await prisma.post.findMany({
where: {
comments: {
every: { text: "interesting" },
},
},
});
- 모든 댓글의 텍스트가 ‘interesting’이어야 포함됨
- 댓글이 하나도 없는 게시글도 조건을 만족한 것으로 간주됨
단일 필드 기준 필터링 시 차이
- 같은 필드 한 개로 필터링해도 결과가 달라질 수 있음
- some은 조건을 만족하는 자식이 하나라도 있으면 부모 포함
- every는 모든 자식이 조건을 만족해야 부모 포함, 자식이 없으면 포함됨
- 자식이 정확히 한 개이고 그 필드만 본다면 두 연산자가 같은 결과를 낼 수 있음. 그러나 자식이 여러 개이거나 없는 경우에는 달라짐
빈 관계를 결과에서 제외하고 싶을 때의 안전한 패턴
const posts = await prisma.post.findMany({
where: {
AND: [
{ comments: { every: { text: "interesting" } } },
{ comments: { some: {} } }, // 최소 1개 존재 보장
],
},
});
- every의 의도를 유지하면서 빈 관계가 포함되는 부작용 제거 가능
베스트 프랙티스
- 의도가 존재성 검사면 some, 전부 일치 검사면 every 선택
- every 사용 시 빈 관계가 포함되는 특성을 인지하고 필요 시 some과 조합하여 최소 존재 보장
- 테스트 데이터에 빈 관계, 혼합된 관계, 전부 일치 관계를 포함해 결과 검증 권장
마무리
some은 하나라도 매칭되면 포함, every는 모두 매칭되어야 포함이라는 원칙. 차이는 자식이 0개 또는 다수일 때 크게 나타남. 단일 필드 기준이라도 항상 동일하지 않음. 관계 특성과 조건 의미를 명확히 정의하고, 빈 관계 처리 여부를 설계 단계에서 결정할 것