Relation queries[Prisma]

SnowCat·2023년 5월 19일
0

Prisma

목록 보기
5/10
post-thumbnail

Nested reads

  • Prisma에서는 데이터베이스의 여러 테이블에서 관련된 정보를 한번에 가져오는 기능을 지원함

  • include 옵션은 쿼리 응답에 join되는 테이블의 일부 필드를 포함해줄 수 있게 함

  • select를 중첩해서 relation field의 특정 값을 가져올 수 있게 함

  • 가령 include문을 사용해 특정한 field를 join할 수 있음

const user = await prisma.user.findFirst({
  include: {
    posts: true,
  },
})

/*
{
  id: 19,
  name: null,
  email: 'emma@prisma.io',
  profileViews: 0,
  role: 'USER',
  coinflips: [],
  posts: [
    {
      id: 20,
      title: 'My first post',
      published: true,
      authorId: 19,
      comments: null,
      views: 0,
      likes: 0
    },
    {
      id: 21,
      title: 'How to make cookies',
      published: true,
      authorId: 19,
      comments: null,
      views: 0,
      likes: 0
    }
  ]
}
*/
  • include를 중첩해서 여러 테이블의 값을 가져올 수도 있음
const user = await prisma.user.findFirst({
  include: {
    posts: {
      include: {
        categories: true,
      },
    },
  },
})

/*
{
    "id": 40,
    "name": "Yvette",
    "email": "yvette@prisma.io",
    "profileViews": 0,
    "role": "USER",
    "coinflips": [],
    "testing": [],
    "city": null,
    "country": "Sweden",
    "posts": [
        {
            "id": 66,
            "title": "How to make an omelette",
            "published": true,
            "authorId": 40,
            "comments": null,
            "views": 0,
            "likes": 0,
            "categories": [
                {
                    "id": 3,
                    "name": "Easy cooking"
                }
            ]
        },
        {
            "id": 67,
            "title": "How to eat an omelette",
            "published": true,
            "authorId": 40,
            "comments": null,
            "views": 0,
            "likes": 0,
            "categories": []
        }
    ]
}
*/
  • select절을 중첩으로 사용해 일부 필드만을 가져올 수도 있음
const user = await prisma.user.findFirst({
  select: {
    name: true,
    posts: {
      select: {
        title: true,
      },
    },
  },
})

/*
{
  name: "Elsa",
  posts: [ { title: 'My first post' }, { title: 'How to make cookies' } ]
}
*/
  • include문과 select문을 중첩해서 사용할수도 있음
const user = await prisma.user.findFirst({
  include: {
    posts: {
      select: {
        title: true,
      },
    },
  },
})

/*
  "id": 1,
  "name": null,
  "email": "martina@prisma.io",
  "profileViews": 0,
  "role": "USER",
  "coinflips": [],
  "posts": [
    { "title": "How to grow salad" },
    { "title": "How to ride a horse" }
  ]
}
*/
  • 단 include와 select는 같은 level에서 사용 불가능하며, 이 때는 중첩 select문을 사용해야 함
// Invalid `prisma.user.findUnique()` invocation:
const user = await prisma.user.findFirst({
  select: { // 같은 level에서 select, include 중복
    email:  true
  }
  include: { // 같은 level에서 select, include 중복
    posts: {
      select: {
        title: true
      }
    }
  },
})

// 아래와 같이 쿼리를 수정해야함
const user = await prisma.user.findFirst({
  select: {
    // This will work!
    email: true,
    posts: {
      select: {
        title: true,
      },
    },
  },
})

Relation count

  • _count 명령어를 사용하면 해당 필드의 갯수를 가져올 수 있음
const relationCount = await prisma.user.findMany({
  include: {
    _count: {
      select: { posts: true },
    },
  },
})

/*
{ id: 1, _count: { posts: 3 } },
{ id: 2, _count: { posts: 2 } },
{ id: 3, _count: { posts: 2 } },
{ id: 4, _count: { posts: 0 } },
{ id: 5, _count: { posts: 0 } }
*/

관계 db의 값을 필터링하기

  • select, include문 내부에 where문을 작성하여 join되는 테이블에 조건을 걸어줄 수 있음
const result = await prisma.user.findFirst({
  select: {
    posts: {
      where: {
        published: false,
      },
      orderBy: {
        title: 'asc',
      },
      select: {
        title: true,
      },
    },
  },
})

Nested writes

  • Prisma에서는 한번에 여러 테이블에 데이터를 작성할 수 있음
  • 쿼리에서 여러 테이블에 걸쳐 데이터를 변경하는 경우 트랜젝션을 보장해줌
  • 데이터 모델에서 지원하는 모든 수준의 중첩을 지원하며, 모델의 생성, 업데이트 쿼리를 관계 필드에 사용할 수 있음

relation record 만들기

  • create문을 중첩으로 사용해 한번에 여러 테이블에 데이터를 생성해줄 수 있음
    createMany문 내부에서는 중첩 불가능함에 유의
const result = await prisma.user.create({
  data: {
    email: 'elsa@prisma.io',
    name: 'Elsa Prisma',
    posts: {
      create: [
        { title: 'How to make an omelette' },
        { title: 'How to eat an omelette' },
      ],
    },
  },
  include: {
    posts: true, // 결과를 반환할 때 posts 값도 포함
  },
})

// 다음과 같은 쿼리는 불가능함
const createMany = await prisma.user.createMany({
  data: [
    {
      name: 'Yewande',
      email: 'yewande@prisma.io',
      posts: {
        // 게시물 생성 불가
      },
    },
    {
      name: 'Noor',
      email: 'noor@prisma.io',
      posts: {
        // 게시물 생성 불가
      },
    },
  ],
})

레코드 연결하기

  • connect 옵션을 사용해 조건에 맞는 데이터를 연결해줄 수 있음
const result = await prisma.user.create({
  data: {
    email: 'vlad@prisma.io',
    posts: {
      connect: [{ id: 8 }, { id: 9 }, { id: 10 }],
    },
  },
  include: {
    posts: true, // Include all posts in the returned object
  },
})
  • 데이터의 존재여부를 모르는 경우에는 connectOrCreate 옵션 사용
const result = await prisma.post.create({
  data: {
    title: 'How to make croissants',
    author: {
      connectOrCreate: {
        where: {
          email: 'viola@prisma.io',
        },
        create: {
          email: 'viola@prisma.io',
          name: 'Viola',
        },
      },
    },
  },
  include: {
    author: true,
  },
})

레코드 연결 해제하기

  • disconnect옵션에 해제할 데이터의 조건을 지정해 관계를 해제해줄 수 있음
const result = await prisma.user.update({
  where: {
    id: 16,
  },
  data: {
    posts: {
      disconnect: [{ id: 12 }, { id: 19 }],
    },
  },
  include: {
    posts: true,
  },
})
  • 하나의 필드의 내용 전체를 해제할 때에는 dissconect: true식으로 사용
const result = await prisma.post.update({
  where: {
    id: 23,
  },
  data: {
    author: {
      disconnect: true,
    },
  },
  include: {
    author: true,
  },
})
  • 모든 관련된 레코드를 해제할 때에는 set 사용
const result = await prisma.user.update({
  where: {
    id: 16,
  },
  data: {
    posts: {
      set: [],
    },
  },
  include: {
    posts: true,
  },
})

관계된 데이터 삭제

  • 데이터 연결 해제를 넘어 데이터를 삭제할 경우에는 delete, deleteMany 옵션을 통해 삭제 진행
const result = await prisma.user.update({
  where: {
    id: 11,
  },
  data: {
    posts: {
      deleteMany: {},
    },
  },
  include: {
    posts: true,
  },
})
  • 내부에 조건을 적어 조건에 만족하는 테이블 전체를 삭제해줄수도 있음
const result = await prisma.user.update({
  where: {
    id: 11,
  },
  data: {
    posts: {
      deleteMany: {
        published: false,
      },
    },
  },
  include: {
    posts: true,
  },
})

const result = await prisma.user.update({
  where: {
    id: 6,
  },
  data: {
    posts: {
      deleteMany: [{ id: 7 }],
    },
  },
  include: {
    posts: true,
  },
})

관련된 레코드 업데이트

  • update, updateMany문을 사용해 관련된 테이블의 데이터를 업데이트해줄 수 있음
const result = await prisma.user.update({
  where: {
    id: 6,
  },
  data: {
    posts: {
      updateMany: {
        where: {
          published: true,
        },
        data: {
          published: false,
        },
      },
    },
  },
  include: {
    posts: true,
  },
})
const result = await prisma.user.update({
  where: {
    id: 6,
  },
  data: {
    posts: {
      update: {
        where: {
          id: 9,
        },
        data: {
          title: 'My updated title',
        },
      },
    },
  },
  include: {
    posts: true,
  },
})
  • 데이터가 있는지 확실하지 않은 경우 upsert 명령어 사용
const result = await prisma.post.update({
  where: {
    id: 6,
  },
  data: {
    author: {
      upsert: {
        create: {
          email: 'bob@prisma.io',
          name: 'Bob the New User',
        },
        update: {
          email: 'bob@prisma.io',
          name: 'Bob the existing user',
        },
      },
    },
  },
  include: {
    author: true,
  },
})

Relation filter

일대다 관계의 필터링

  • Prisma에서는 일대다 관계가 있을 경우 some, none, every 조건을 사용해 데이터를 필터링 할 수 있음
    some 조건은 하나 이상의 데이터가 조건을 만족해야 함
    none 조건은 어떠한 데이터도 조건을 만족해서는 안됨
    every 조건은 모든 데이터가 조건을 만족해야 함을 의미함
// 조회수가 100회 이상인 게시물이 없고, 모든 게시물의 좋아요는 50개 이하
const users = await prisma.user.findMany({
  where: {
    posts: {
      none: {
        views: {
          gt: 100,
        },
      },
      every: {
        likes: {
          lte: 50,
        },
      },
    },
  },
  include: {
    posts: true,
  },
})

일대일 관계의 필터링

  • 일대일 관계인 경우 is, isNot을 사용해 데이터를 필터링 해줄 수 있음
// 이름이 Bob이 아니며, 40세 이상인 모든 유저를 출력하는 쿼리
const users = await prisma.post.findMany({
  where: {
    author: {
      isNot: {
        name: 'Bob',
      },
      is: {
        age: {
          gt: 40,
        },
      },
    },
  },
  include: {
    author: true,
  },
})

관련 레코드 존재 여부 확인

  • 관계가 없는 경우를 확인하는 경우에는 null 사용
const postsWithNoAuthor = await prisma.post.findMany({
  where: {
    author: null, // or author: { }
  },
  include: {
    author: true,
  },
})

출처:
https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries

profile
냐아아아아아아아아앙

0개의 댓글